Removal of legacy code
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "../../node_modules/rome/configuration_schema.json",
|
||||
"$schema": "./node_modules/rome/configuration_schema.json",
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
|
@ -13,5 +13,11 @@
|
|||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"files": {
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
5
.vscode/settings.json
vendored
|
@ -3,5 +3,8 @@
|
|||
"editor.defaultFormatter": "trunk.io",
|
||||
"files.associations": {
|
||||
"*.mdx": "markdown"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "rome.rome"
|
||||
}
|
||||
}
|
||||
}
|
24
package.json
|
@ -18,32 +18,26 @@
|
|||
"@docusaurus/preset-classic": "3.0.0-alpha.0",
|
||||
"@docusaurus/theme-common": "3.0.0-alpha.0",
|
||||
"@docusaurus/theme-mermaid": "3.0.0-alpha.0",
|
||||
"@headlessui/react": "^1.7.16",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@meshtastic/meshtasticjs": "2.2.0-0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"@meshtastic/meshtasticjs": "2.2.4-0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"base64-js": "^1.5.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"framer-motion": "^6.5.1",
|
||||
"postcss": "^8.4.27",
|
||||
"postcss": "^8.4.29",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.10.1",
|
||||
"react-responsive-carousel": "^3.2.23",
|
||||
"swr": "^2.2.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"url-search-params-polyfill": "^8.2.4",
|
||||
"use-breakpoint": "^3.1.1"
|
||||
"react-icons": "^4.11.0",
|
||||
"swr": "^2.2.2",
|
||||
"tailwindcss": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.0.0-alpha.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tsconfig/docusaurus": "^2.0.0",
|
||||
"@types/node": "^20.4.8",
|
||||
"@types/react": "^18.2.18",
|
||||
"@types/node": "^20.6.0",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"rome": "^12.1.3",
|
||||
"typescript": "^5.1.6"
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
|
4059
pnpm-lock.yaml
|
@ -1,27 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,13 +1,13 @@
|
|||
import React from "react";
|
||||
|
||||
export interface ColorModeProps {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Dark = ({ children }: ColorModeProps): JSX.Element => {
|
||||
return <div className="hideLight">{children}</div>;
|
||||
return <div className="hideLight">{children}</div>;
|
||||
};
|
||||
|
||||
export const Light = ({ children }: ColorModeProps): JSX.Element => {
|
||||
return <div className="hideDark">{children}</div>;
|
||||
return <div className="hideDark">{children}</div>;
|
||||
};
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
// 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>
|
||||
// );
|
||||
// };
|
|
@ -1,49 +0,0 @@
|
|||
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"
|
||||
>
|
||||
​
|
||||
</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>
|
||||
);
|
||||
};
|
|
@ -3,23 +3,23 @@ import React from "react";
|
|||
import Layout from "@theme/Layout";
|
||||
|
||||
export interface PageLayoutProps {
|
||||
title: string;
|
||||
description: string;
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const PageLayout = ({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
}: PageLayoutProps): JSX.Element => {
|
||||
return (
|
||||
<Layout title={title} description={description}>
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
return (
|
||||
<Layout title={title} description={description}>
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ColorModeProps {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
export interface BadgeProps {
|
||||
name: string;
|
||||
color: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Badge = ({ name, color, icon }: BadgeProps): JSX.Element => {
|
||||
return (
|
||||
<div
|
||||
className={`flex h-min cursor-pointer gap-1 rounded-md px-1 text-white shadow-md hover:opacity-80 ${color}`}
|
||||
>
|
||||
<div className="my-1">{icon}</div>
|
||||
<span className="hidden truncate md:flex">{name}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
||||
export interface CardTabProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const CardTab = ({ title }: CardTabProps): JSX.Element => {
|
||||
return (
|
||||
<Tab
|
||||
className={({ selected }) =>
|
||||
`w-1/3 truncate rounded-md px-3 py-2 text-sm font-medium hover:bg-tertiary ${
|
||||
selected ? "bg-secondary shadow-md" : ""
|
||||
}`
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</Tab>
|
||||
);
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { IDevice, Stability } from "../../data/device";
|
||||
|
||||
export interface HardwareCardProps {
|
||||
device: IDevice;
|
||||
setDevice: () => void;
|
||||
}
|
||||
|
||||
export const HardwareCard = ({
|
||||
device,
|
||||
setDevice,
|
||||
}: HardwareCardProps): JSX.Element => {
|
||||
return (
|
||||
<li
|
||||
className="group relative"
|
||||
onClick={() => {
|
||||
setDevice();
|
||||
}}
|
||||
onKeyDown={() => {
|
||||
setDevice();
|
||||
}}
|
||||
>
|
||||
<div className="overflow-hidden rounded-lg">
|
||||
<div
|
||||
className={`flex aspect-[4/3] overflow-hidden ${device.misc.Gradient}`}
|
||||
>
|
||||
<img
|
||||
src={device.images.Front}
|
||||
alt=""
|
||||
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2 group-hover:opacity-75"
|
||||
/>
|
||||
</div>
|
||||
<button type="button" className="absolute inset-0 focus:outline-none">
|
||||
<span className="sr-only">View details for {device.name}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div>
|
||||
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
||||
{device.name}
|
||||
</p>
|
||||
<p className="pointer-events-none flex gap-1 text-sm font-medium text-mute">
|
||||
<div
|
||||
className={`my-auto h-3 w-3 rounded-full ${
|
||||
device.misc.Stability === Stability.Broken
|
||||
? "bg-red-500"
|
||||
: device.misc.Stability === Stability.Unstable
|
||||
? "bg-orange-500"
|
||||
: device.misc.Stability === Stability.Semi
|
||||
? "bg-cyan-500"
|
||||
: "bg-green-500"
|
||||
}`}
|
||||
/>
|
||||
<div className="my-auto">{Stability[device.misc.Stability]}</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
};
|
|
@ -1,173 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { FiBluetooth, FiChevronRight, FiWifi, FiX } from "react-icons/fi";
|
||||
import { useBreakpoint } from "use-breakpoint";
|
||||
|
||||
import { Tab } from "@headlessui/react";
|
||||
import type { IDevice } from "../../data/device";
|
||||
|
||||
import { Button } from "../../components/Button";
|
||||
import { BREAKPOINTS } from "../../utils/breakpoints";
|
||||
import { Modal } from "../Modal";
|
||||
import { Badge } from "./Badge";
|
||||
import { CardTab } from "./CardTab";
|
||||
import { InfoTab } from "./Tabs/InfoTab";
|
||||
import { PinoutTab } from "./Tabs/PinoutTab";
|
||||
import { PowerTab } from "./Tabs/PowerTab";
|
||||
import { VariantSelectButton } from "./VariantSelectButton";
|
||||
|
||||
export interface HardwareModal {
|
||||
device: IDevice;
|
||||
open: boolean;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export const HardwareModal = ({
|
||||
device,
|
||||
open,
|
||||
close,
|
||||
}: HardwareModal): JSX.Element => {
|
||||
const [hideDetails, setHideDetails] = useState(false);
|
||||
const { breakpoint } = useBreakpoint(BREAKPOINTS, "md");
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={close}>
|
||||
<div className="absolute right-0 z-20 m-2 md:flex">
|
||||
<Button onClick={close}>
|
||||
<FiX />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="absolute inset-0">
|
||||
<motion.div
|
||||
layout
|
||||
animate={
|
||||
breakpoint === "sm"
|
||||
? hideDetails
|
||||
? "hiddenSm"
|
||||
: "visibleSm"
|
||||
: hideDetails
|
||||
? "hidden"
|
||||
: "visible"
|
||||
}
|
||||
variants={{
|
||||
hidden: { width: "100%", height: "100%" },
|
||||
hiddenSm: { height: "100%", width: "100%" },
|
||||
visible: { width: "20%", height: "100%" },
|
||||
visibleSm: { height: "33%", width: "100%" },
|
||||
}}
|
||||
transition={{
|
||||
type: "just",
|
||||
}}
|
||||
className="flex flex-col md:h-full md:flex-row"
|
||||
>
|
||||
<motion.div
|
||||
layout
|
||||
className={`relative z-10 flex h-full w-full rounded-t-2xl md:rounded-l-2xl md:rounded-tr-none ${device.misc.Gradient}`}
|
||||
>
|
||||
<motion.img
|
||||
layout
|
||||
src={device.images.Front}
|
||||
alt=""
|
||||
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2"
|
||||
/>
|
||||
<div className="absolute -bottom-5 z-20 flex w-full md:bottom-auto md:-right-5 md:h-full md:w-auto">
|
||||
<Button
|
||||
animate={
|
||||
breakpoint === "sm"
|
||||
? hideDetails
|
||||
? "hiddenSm"
|
||||
: "visibleSm"
|
||||
: hideDetails
|
||||
? "hidden"
|
||||
: "visible"
|
||||
}
|
||||
variants={{
|
||||
hidden: { rotate: 180 },
|
||||
hiddenSm: { rotate: -90 },
|
||||
visible: { rotate: 0 },
|
||||
visibleSm: { rotate: 90 },
|
||||
}}
|
||||
onClick={() => {
|
||||
setHideDetails(!hideDetails);
|
||||
}}
|
||||
>
|
||||
<FiChevronRight />
|
||||
</Button>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{!hideDetails && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className={`absolute -bottom-5 z-20 flex md:mt-0 md:hidden md:pb-2 ${
|
||||
hideDetails ? "opacity-0" : "opacity-100"
|
||||
}`}
|
||||
>
|
||||
<VariantSelectButton options={device.variants} />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="absolute -bottom-3 right-0 m-auto mr-2 ml-auto flex md:inset-x-1 md:bottom-4 md:mt-2"
|
||||
>
|
||||
<div className="m-auto flex gap-2">
|
||||
{device.features.BLE && (
|
||||
<Badge
|
||||
name="Bluetooth"
|
||||
color="bg-blue-500"
|
||||
icon={<FiBluetooth />}
|
||||
/>
|
||||
)}
|
||||
{device.features.WiFi && (
|
||||
<Badge
|
||||
name="WiFi"
|
||||
color="bg-orange-500"
|
||||
icon={<FiWifi />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
<div
|
||||
className={`h-7 bg-base opacity-0 md:h-auto md:w-7 ${
|
||||
hideDetails ? "flex" : "hidden"
|
||||
}`}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
<div className="z-[1] mt-[25%] flex h-full flex-col md:ml-[20%] md:mt-0 md:w-4/5">
|
||||
<div className="z-0 hidden pb-2 md:flex">
|
||||
<VariantSelectButton options={device.variants} />
|
||||
</div>
|
||||
<div
|
||||
className={`mt-1 flex flex-grow rounded-2xl bg-base p-2 shadow-inner transition-opacity duration-100 ease-linear md:mt-0 md:rounded-l-none md:rounded-r-2xl md:p-4 ${
|
||||
hideDetails ? "opacity-0" : "opacity-100"
|
||||
}`}
|
||||
>
|
||||
<Tab.Group
|
||||
as="div"
|
||||
className="flex flex-grow flex-col rounded-2xl bg-primary p-2"
|
||||
>
|
||||
<Tab.List className="flex gap-2">
|
||||
<CardTab title="Info" />
|
||||
<CardTab title="Power" />
|
||||
<CardTab title="Pinout" />
|
||||
</Tab.List>
|
||||
<Tab.Panels as="div" className="flex-grow overflow-y-auto">
|
||||
<InfoTab device={device} />
|
||||
<PowerTab device={device} />
|
||||
<PinoutTab device={device} />
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
||||
import type { IDevice } from "../../../data/device";
|
||||
|
||||
export interface InfoTabProps {
|
||||
device: IDevice;
|
||||
}
|
||||
|
||||
export const InfoTab = ({ device }: InfoTabProps): JSX.Element => {
|
||||
return (
|
||||
<Tab.Panel>
|
||||
<div className="px-4 py-5 sm:p-0">
|
||||
<dl className="sm:divide-y sm:divide-gray-200">
|
||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
||||
<dt className="text-sm font-medium text-secondaryInv">
|
||||
BLE/WiFi Version
|
||||
</dt>
|
||||
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
||||
<span className="rounded-md bg-secondary px-0.5">
|
||||
{device.specifications.BLEVersion}
|
||||
</span>
|
||||
/
|
||||
<span className="rounded-md bg-secondary px-0.5">
|
||||
{device.specifications.WiFiVersion}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
||||
<dt className="text-sm font-medium text-secondaryInv">
|
||||
BLE/WiFi Antenna
|
||||
</dt>
|
||||
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
||||
<span className="rounded-md bg-secondary px-0.5">
|
||||
{device.specifications.BLEAntenna}
|
||||
</span>
|
||||
/
|
||||
<span className="rounded-md bg-secondary px-0.5">
|
||||
{device.specifications.WiFiAntenna}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
|
@ -1,36 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
||||
import type { IDevice } from "../../../data/device";
|
||||
|
||||
export interface PinoutTabProps {
|
||||
device: IDevice;
|
||||
}
|
||||
|
||||
export const PinoutTab = ({ device }: PinoutTabProps): JSX.Element => {
|
||||
return (
|
||||
<Tab.Panel className="flex">
|
||||
<div className="m-auto flex gap-4 rounded-lg bg-slate-700 px-2 py-1 shadow-md">
|
||||
{[
|
||||
device.pinout.slice(0, device.misc.pinoutSplit),
|
||||
device.pinout.slice(device.misc.pinoutSplit, device.pinout.length),
|
||||
].map((group, index) => (
|
||||
<div key={index}>
|
||||
{group.map((pin, pinIndex) => (
|
||||
<div
|
||||
className={`flex gap-1 ${
|
||||
index === 0 ? "flex-row" : "flex-row-reverse"
|
||||
}`}
|
||||
key={pinIndex}
|
||||
>
|
||||
<div className="m-auto h-3 w-3 rounded-full border bg-yellow-500" />
|
||||
<span className="m-auto font-mono text-white">{pin.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
||||
import type { IDevice } from "../../../data/device";
|
||||
|
||||
export interface PowerTabProps {
|
||||
device: IDevice;
|
||||
}
|
||||
|
||||
export const PowerTab = (): JSX.Element => {
|
||||
return <Tab.Panel className="h-32">Content 1</Tab.Panel>;
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
import React, { Fragment, useState } from "react";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { FiCheck } from "react-icons/fi";
|
||||
import { HiSelector } from "react-icons/hi";
|
||||
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import type { Variant } from "../../data/device";
|
||||
|
||||
export interface VariantSelectButtonProps {
|
||||
options: Variant[];
|
||||
}
|
||||
|
||||
export const VariantSelectButton = ({
|
||||
options,
|
||||
}: VariantSelectButtonProps): JSX.Element => {
|
||||
const [selected, setSelected] = useState(options[options.length - 1]);
|
||||
|
||||
return (
|
||||
<Listbox value={selected} onChange={setSelected}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="relative select-none">
|
||||
<Listbox.Button as={Fragment}>
|
||||
<motion.button
|
||||
whileHover={{ backgroundColor: "var(--tertiary)" }}
|
||||
whileTap={{ scale: 0.99 }}
|
||||
className="relative -mt-5 ml-2 flex w-fit gap-1 rounded-lg bg-secondary p-2 py-2 pl-3 pr-10 text-lg font-medium leading-6 shadow-md md:mt-2"
|
||||
>
|
||||
<span className="block truncate">{selected.name}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<HiSelector
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</motion.button>
|
||||
</Listbox.Button>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-primary py-1 shadow-md ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||
{options.map((variant, index) => (
|
||||
<Listbox.Option
|
||||
key={index}
|
||||
className={({ active }) =>
|
||||
`relative cursor-default select-none py-2 pl-3 pr-9 ${
|
||||
active ? "bg-secondary" : ""
|
||||
}`
|
||||
}
|
||||
value={variant}
|
||||
>
|
||||
{({ selected, active }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? "font-semibold" : "font-normal"
|
||||
}`}
|
||||
>
|
||||
{variant.name}
|
||||
</span>
|
||||
|
||||
{selected ? (
|
||||
<span
|
||||
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
|
||||
active ? "" : "text-primaryInv"
|
||||
}`}
|
||||
>
|
||||
<FiCheck className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Listbox>
|
||||
);
|
||||
};
|
|
@ -3,29 +3,29 @@ import React from "react";
|
|||
import { FiExternalLink } from "react-icons/fi";
|
||||
|
||||
export interface SocialCardProps {
|
||||
children: React.ReactNode;
|
||||
color: string;
|
||||
link: string;
|
||||
children: React.ReactNode;
|
||||
color: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export const SocialCard = ({
|
||||
children,
|
||||
color,
|
||||
link,
|
||||
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>
|
||||
);
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,345 +1,345 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Protobuf, Types } from "@meshtastic/meshtasticjs";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
interface Region {
|
||||
freq_start: number;
|
||||
freq_end: number;
|
||||
duty_cycle: number;
|
||||
spacing: number;
|
||||
power_limit: number;
|
||||
freq_start: number;
|
||||
freq_end: number;
|
||||
duty_cycle: number;
|
||||
spacing: number;
|
||||
power_limit: number;
|
||||
}
|
||||
|
||||
interface Modem {
|
||||
bw: number;
|
||||
cr: number;
|
||||
sf: number;
|
||||
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,
|
||||
},
|
||||
],
|
||||
[
|
||||
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,
|
||||
},
|
||||
],
|
||||
[
|
||||
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);
|
||||
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),
|
||||
);
|
||||
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);
|
||||
setNumChannels(calculatedNumChannels);
|
||||
|
||||
let updatedChannel = channel;
|
||||
if (updatedChannel >= calculatedNumChannels) {
|
||||
updatedChannel = 0;
|
||||
}
|
||||
let updatedChannel = channel;
|
||||
if (updatedChannel >= calculatedNumChannels) {
|
||||
updatedChannel = 0;
|
||||
}
|
||||
|
||||
setChannel(updatedChannel);
|
||||
setChannel(updatedChannel);
|
||||
|
||||
setChannelFrequency(
|
||||
selectedRegion.freq_start +
|
||||
selectedModemPreset.bw / 2000 +
|
||||
updatedChannel * (selectedModemPreset.bw / 1000),
|
||||
);
|
||||
}, [modemPreset, region, channel]);
|
||||
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>
|
||||
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>
|
||||
);
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
export type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
|
||||
export enum UseCase {
|
||||
Solar = 0,
|
||||
Router = 1,
|
||||
Portable = 2,
|
||||
}
|
||||
|
||||
enum PinUsage {
|
||||
LoRa = 0,
|
||||
GNSS = 1,
|
||||
}
|
||||
|
||||
export interface Pin {
|
||||
offset: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
name: string;
|
||||
label: string;
|
||||
usage?: PinUsage;
|
||||
}
|
||||
|
||||
export type DeviceName = "tbeam" | "techo";
|
||||
|
||||
export type BLEVersion = "4.2" | "5.0";
|
||||
|
||||
export type AntennaType = "Integrated";
|
||||
|
||||
export type Chipset = "ESP32" | "NRF52";
|
||||
|
||||
export type Frequency = 433 | 868 | 915 | 923;
|
||||
|
||||
export type SerialAdapter = "CP210X" | "CH9102";
|
||||
|
||||
export type GNSSModule = "NEO-6M" | "NEO-8M";
|
||||
|
||||
export type LORAModule = "SX1276" | "SX1262";
|
||||
|
||||
export type Variant = DeepPartial<Omit<IDevice, "variants">> & { name: string };
|
||||
|
||||
export enum Stability {
|
||||
Stable = 0,
|
||||
Semi = 1,
|
||||
Unstable = 2,
|
||||
Broken = 3,
|
||||
}
|
||||
|
||||
export type Module =
|
||||
| "cannedMessage"
|
||||
| "externalNotification"
|
||||
| "rangeTest"
|
||||
| "rotaryEncoder"
|
||||
| "storeAndForward"
|
||||
| "telemetry";
|
||||
|
||||
export interface IDevice {
|
||||
name: string;
|
||||
misc: {
|
||||
SuggestedUse: UseCase[];
|
||||
Stability: Stability;
|
||||
Gradient: string;
|
||||
pinoutSplit: number;
|
||||
};
|
||||
images: {
|
||||
Front: string;
|
||||
Back: string;
|
||||
};
|
||||
features: {
|
||||
BLE: boolean;
|
||||
WiFi: boolean;
|
||||
Modules: Module[];
|
||||
};
|
||||
specifications: {
|
||||
BLEVersion?: BLEVersion;
|
||||
BLEAntenna?: AntennaType;
|
||||
WiFiVersion?: string;
|
||||
WiFiAntenna?: AntennaType;
|
||||
Chipset: Chipset;
|
||||
Driver: SerialAdapter;
|
||||
GNSS?: GNSSModule;
|
||||
FlashSize: number;
|
||||
Frequencies: Frequency[];
|
||||
LoRa: LORAModule;
|
||||
PSRAM: number;
|
||||
RAM?: number;
|
||||
};
|
||||
pinout: Pin[];
|
||||
variants: Variant[];
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const heltec: IDevice = {
|
||||
name: "Heltec",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/heltec-v2.png",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const hydra: IDevice = {
|
||||
name: "Hydra",
|
||||
misc: {
|
||||
Stability: Stability.Stable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-indigo-200 via-red-200 to-yellow-100",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/Hydra-PCB.2.1.svg",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const nano_g1: IDevice = {
|
||||
name: "Nano G1",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-green-200 to-green-500",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/nano_g1_front.svg",
|
||||
Back: "/img/hardware/nano_g1_back.svg",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const rak19001: IDevice = {
|
||||
name: "WisBlock 19001",
|
||||
misc: {
|
||||
Stability: Stability.Stable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-indigo-300 to-purple-400",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/rak/RAK19001.png",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const rak19003: IDevice = {
|
||||
name: "WisBlock 19003",
|
||||
misc: {
|
||||
Stability: Stability.Stable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-b from-orange-500 to-yellow-300",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/rak/RAK19003.png",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,277 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const tbeam: IDevice = {
|
||||
name: "T-Beam",
|
||||
misc: {
|
||||
Stability: Stability.Stable,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500",
|
||||
pinoutSplit: 13,
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/tbeam-v1.1.svg",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
pinout: [
|
||||
{
|
||||
label: "VP",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "VN",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "RST",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "15",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "35",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "32",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "33",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "25",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "14",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "13",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "2",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "GND",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "5V",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "TX",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "RX",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "23",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "4",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "0",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "GND",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "3V3",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "GND",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "22",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "21",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "3.3V",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "LoRa2",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "LoRa1",
|
||||
name: "IO1",
|
||||
offset: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
import { IDevice, Stability, UseCase } from "../device";
|
||||
|
||||
export const techo: IDevice = {
|
||||
name: "T-Echo",
|
||||
misc: {
|
||||
Stability: Stability.Semi,
|
||||
SuggestedUse: [UseCase.Portable],
|
||||
Gradient: "bg-gradient-to-r from-gray-700 via-gray-900 to-black",
|
||||
},
|
||||
images: {
|
||||
Front: "/img/hardware/t-echo-lilygo.jpg",
|
||||
Back: "",
|
||||
},
|
||||
features: {
|
||||
BLE: true,
|
||||
WiFi: true,
|
||||
Modules: [
|
||||
"cannedMessage",
|
||||
"externalNotification",
|
||||
"rangeTest",
|
||||
"rotaryEncoder",
|
||||
"storeAndForward",
|
||||
"telemetry",
|
||||
],
|
||||
},
|
||||
specifications: {
|
||||
BLEVersion: "4.2",
|
||||
BLEAntenna: "Integrated",
|
||||
WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
|
||||
WiFiAntenna: "Integrated",
|
||||
Chipset: "ESP32",
|
||||
Driver: "CH9102",
|
||||
GNSS: "NEO-6M",
|
||||
FlashSize: 4,
|
||||
Frequencies: [433, 868, 915, 923],
|
||||
LoRa: "SX1262",
|
||||
PSRAM: 8,
|
||||
RAM: undefined,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
name: "TBeam 0.7",
|
||||
misc: {
|
||||
Stability: Stability.Unstable,
|
||||
},
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.0",
|
||||
specifications: {
|
||||
Frequencies: [868, 915],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TBeam 1.1",
|
||||
specifications: {
|
||||
Driver: "CP210X",
|
||||
GNSS: "NEO-6M",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
export interface IRegion {
|
||||
name: string;
|
||||
freqStart: number;
|
||||
freqEnd: number;
|
||||
dutyCycle: number;
|
||||
spacing: number;
|
||||
powerLimit: number;
|
||||
audioPermitted: boolean;
|
||||
frequencySwitching: boolean;
|
||||
wideLora: boolean;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const ANZ: IRegion = {
|
||||
name: "ANZ",
|
||||
freqStart: 915.0,
|
||||
freqEnd: 928.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 30,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const CN: IRegion = {
|
||||
name: "CN",
|
||||
freqStart: 470.0,
|
||||
freqEnd: 510.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 19,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const EU_433: IRegion = {
|
||||
name: "EU_433",
|
||||
freqStart: 433.0,
|
||||
freqEnd: 434.0,
|
||||
dutyCycle: 10,
|
||||
spacing: 0,
|
||||
powerLimit: 12,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const EU_868: IRegion = {
|
||||
name: "EU_868",
|
||||
freqStart: 869.4,
|
||||
freqEnd: 869.65,
|
||||
dutyCycle: 10,
|
||||
spacing: 0,
|
||||
powerLimit: 27,
|
||||
audioPermitted: false,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const IN: IRegion = {
|
||||
name: "IN",
|
||||
freqStart: 865.0,
|
||||
freqEnd: 867.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 30,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const JP: IRegion = {
|
||||
name: "JP",
|
||||
freqStart: 920.8,
|
||||
freqEnd: 927.8,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 16,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const KR: IRegion = {
|
||||
name: "KR",
|
||||
freqStart: 920.0,
|
||||
freqEnd: 925.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 0,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const LORA_24: IRegion = {
|
||||
name: "LORA_24",
|
||||
freqStart: 2400.0,
|
||||
freqEnd: 2483.5,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 10,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: true,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const NZ_865: IRegion = {
|
||||
name: "NZ_865",
|
||||
freqStart: 864.0,
|
||||
freqEnd: 868.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 36,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const RU: IRegion = {
|
||||
name: "RU",
|
||||
freqStart: 868.7,
|
||||
freqEnd: 869.2,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 20,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const TH: IRegion = {
|
||||
name: "TH",
|
||||
freqStart: 920.0,
|
||||
freqEnd: 925.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 16,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const TW: IRegion = {
|
||||
name: "TW",
|
||||
freqStart: 920.0,
|
||||
freqEnd: 925.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 0,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const UA_433: IRegion = {
|
||||
name: "UA_433",
|
||||
freqStart: 433.0,
|
||||
freqEnd: 434.7,
|
||||
dutyCycle: 10,
|
||||
spacing: 0,
|
||||
powerLimit: 10,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const UA_868: IRegion = {
|
||||
name: "UA_868",
|
||||
freqStart: 868.0,
|
||||
freqEnd: 868.6,
|
||||
dutyCycle: 1,
|
||||
spacing: 0,
|
||||
powerLimit: 14,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const UNSET: IRegion = {
|
||||
name: "UNSET",
|
||||
freqStart: 902.0,
|
||||
freqEnd: 928.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 30,
|
||||
audioPermitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { IRegion } from "../region";
|
||||
|
||||
export const US: IRegion = {
|
||||
name: "US",
|
||||
freqStart: 902.0,
|
||||
freqEnd: 928.0,
|
||||
dutyCycle: 100,
|
||||
spacing: 0,
|
||||
powerLimit: 30,
|
||||
audioPrmitted: true,
|
||||
frequencySwitching: false,
|
||||
wideLora: false,
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
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],
|
||||
);
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
};
|
|
@ -1,458 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import { FiTwitter } from "react-icons/fi";
|
||||
|
||||
import { ChevronRightIcon } from "@heroicons/react/20/solid";
|
||||
import Layout from "@theme/Layout";
|
||||
import { Dark, Light } from "../../components/ColorMode";
|
||||
|
||||
const TwoPointZero = (): JSX.Element => {
|
||||
const stats = [
|
||||
{ label: "Active Nodes", value: "A Lot!" },
|
||||
{ label: "Community Members", value: "4000+" },
|
||||
{ label: "Firmware Commits", value: "4900+" },
|
||||
{ label: "Community Donations", value: "$5700+" },
|
||||
];
|
||||
const logos = [
|
||||
{
|
||||
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="Meshtastic 2.0" description="Meshtastic 2.0 Landing Page">
|
||||
<div>
|
||||
<main>
|
||||
{/* Hero section */}
|
||||
<div className="overflow-hidden pt-8 sm:pt-12 lg:relative lg:py-24">
|
||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-2 lg:gap-24 lg:px-8">
|
||||
<div>
|
||||
<div>
|
||||
<Dark>
|
||||
<img
|
||||
className="h-11 w-auto"
|
||||
src="/design/logo/svg/Mesh_Logo_White.svg"
|
||||
alt="Meshtastic Logo"
|
||||
/>
|
||||
</Dark>
|
||||
<Light>
|
||||
<img
|
||||
className="h-11 w-auto"
|
||||
src="/design/logo/svg/Mesh_Logo_Black.svg"
|
||||
alt="Meshtastic Logo"
|
||||
/>
|
||||
</Light>
|
||||
</div>
|
||||
<div className="mt-20">
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/meshtastic/firmware/releases"
|
||||
className="inline-flex space-x-4"
|
||||
>
|
||||
<span className="rounded bg-rose-50 px-2.5 py-1 text-sm font-semibold text-rose-500">
|
||||
What's new
|
||||
</span>
|
||||
<span className="inline-flex items-center space-x-1 text-sm font-medium text-rose-500">
|
||||
<span>Click to find out</span>
|
||||
<ChevronRightIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-6 sm:max-w-xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight --ifm-heading-color sm:text-5xl">
|
||||
Meshtastic 2.0 🎉🎉🎉
|
||||
</h1>
|
||||
<p className="mt-6 text-xl --ifm-font-color-base">
|
||||
After 9 months in the making, we present to you the next
|
||||
milestone for the Meshtastic project.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-12 sm:w-full sm:max-w-lg">
|
||||
<p className="mt-6 mb-4 text-xl --ifm-font-color-base">
|
||||
As a part of the launch event, we are running a number of
|
||||
giveaways, so jump in and win some prizes.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<a
|
||||
href="#start"
|
||||
className="flex w-full rounded-md border border-transparent bg-rose-500 px-5 py-3 font-medium text-white shadow hover:bg-rose-600 hover:text-black hover:no-underline focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 sm:px-10"
|
||||
>
|
||||
<span className="m-auto">Find Out More</span>
|
||||
</a>
|
||||
<a
|
||||
className="flex w-16 rounded-md border border-transparent bg-[#1DA1F2] shadow hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://twitter.com/TheMeshtastic/status/1586933393526333441"
|
||||
>
|
||||
<FiTwitter className="m-auto text-white" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:mx-auto sm:max-w-3xl sm:px-6">
|
||||
<div className="py-12 sm:relative sm:mt-12 sm:py-16 lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
||||
<div className="hidden sm:block">
|
||||
<div className="absolute inset-y-0 left-1/2 w-screen rounded-l-3xl bg-gray-50 lg:left-80 lg:right-0 lg:w-full" />
|
||||
<svg
|
||||
className="absolute top-8 right-1/2 -mr-3 lg:left-0 lg:m-0"
|
||||
width={404}
|
||||
height={392}
|
||||
fill="none"
|
||||
viewBox="0 0 404 392"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="837c3e70-6c3a-44e6-8854-cc48c737b659"
|
||||
x={0}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
width={404}
|
||||
height={392}
|
||||
fill="url(#837c3e70-6c3a-44e6-8854-cc48c737b659)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="relative -mr-40 pl-4 sm:mx-auto sm:max-w-3xl sm:px-0 lg:h-full lg:max-w-none lg:pl-12">
|
||||
<img
|
||||
className="w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||
src="/2.0/webUI.png"
|
||||
alt="Web UI"
|
||||
/>
|
||||
<img
|
||||
className="absolute top-0 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||
src="/2.0/android.jpg"
|
||||
alt="Android"
|
||||
/>
|
||||
<img
|
||||
className="absolute top-0 left-96 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||
src="/2.0/ios.png"
|
||||
alt="IOS"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Testimonial/stats section */}
|
||||
<div className="relative mt-20">
|
||||
<div className="lg:mx-auto lg:grid lg:max-w-7xl lg:grid-cols-2 lg:items-start lg:gap-24 lg:px-8">
|
||||
<div className="relative sm:py-16 lg:py-0">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="hidden sm:block lg:absolute lg:inset-y-0 lg:right-0 lg:w-screen"
|
||||
>
|
||||
<div className="absolute inset-y-0 right-1/2 w-full rounded-r-3xl bg-gray-50 lg:right-72" />
|
||||
<svg
|
||||
className="absolute top-8 left-1/2 -ml-3 lg:-right-8 lg:left-auto lg:top-12"
|
||||
width={404}
|
||||
height={392}
|
||||
fill="none"
|
||||
viewBox="0 0 404 392"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="02f20b47-fd69-4224-a62a-4c9de5c763f7"
|
||||
x={0}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
width={404}
|
||||
height={392}
|
||||
fill="url(#02f20b47-fd69-4224-a62a-4c9de5c763f7)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-none lg:px-0 lg:py-20">
|
||||
{/* Testimonial card*/}
|
||||
<div className="relative overflow-hidden rounded-2xl pt-64 pb-10 shadow-xl">
|
||||
<img
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
src="/2.0/background.png"
|
||||
alt=""
|
||||
/>
|
||||
<div className="absolute inset-0 bg-green-500 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-green-600 via-green-600 opacity-90" />
|
||||
<div className="relative px-8">
|
||||
<div>
|
||||
<img
|
||||
className="h-8 text-white"
|
||||
src="/2.0/typelogo.svg"
|
||||
alt="Meshtastic"
|
||||
/>
|
||||
</div>
|
||||
<blockquote className="mt-8">
|
||||
<div className="relative text-lg font-medium text-white md:flex-grow">
|
||||
<svg
|
||||
className="absolute top-0 left-0 h-8 w-8 -translate-x-3 -translate-y-2 transform text-rose-400"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M9.352 4C4.456 7.456 1 13.12 1 19.36c0 5.088 3.072 8.064 6.624 8.064 3.36 0 5.856-2.688 5.856-5.856 0-3.168-2.208-5.472-5.088-5.472-.576 0-1.344.096-1.536.192.48-3.264 3.552-7.104 6.624-9.024L9.352 4zm16.512 0c-4.8 3.456-8.256 9.12-8.256 15.36 0 5.088 3.072 8.064 6.624 8.064 3.264 0 5.856-2.688 5.856-5.856 0-3.168-2.304-5.472-5.184-5.472-.576 0-1.248.096-1.44.192.48-3.264 3.456-7.104 6.528-9.024L25.864 4z" />
|
||||
</svg>
|
||||
<p className="relative">
|
||||
Meshtastic is the neatest open source project I've
|
||||
ever seen!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<footer className="mt-4">
|
||||
<p className="text-base font-semibold text-rose-200">
|
||||
Elvis Presley
|
||||
</p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="start"
|
||||
className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:px-0"
|
||||
>
|
||||
{/* Content area */}
|
||||
<div className="pt-12 sm:pt-16 lg:pt-20">
|
||||
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
||||
A brief overview of all the changes and improvements
|
||||
</h2>
|
||||
<div className="mt-6 space-y-6 --ifm-font-color-base">
|
||||
<h2>Monumental stuff!</h2>
|
||||
<p className="leading-7 --ifm-font-color-base">
|
||||
<li>
|
||||
Completely new LoRA band plan with faster messaging
|
||||
</li>
|
||||
<li>Smarter and more reliable mesh routing</li>
|
||||
<li>
|
||||
Unlimited nodes* (80 Connected at a time, oldest node
|
||||
will be removed when a new node joins the mesh)
|
||||
</li>
|
||||
<li>
|
||||
New messaging additions: waypoints, reactions
|
||||
(tap-backs), and telemetry
|
||||
</li>
|
||||
<li>
|
||||
Improvements for Canned Messages module and CardKB
|
||||
messaging for stand alone communicator devices
|
||||
</li>
|
||||
<li>Sensor, Screen, and Input device auto-detection</li>
|
||||
<li>New devices supported (6 new targets!)</li>
|
||||
<li>
|
||||
Added over the air bluetooth updates for NRF devices
|
||||
(RAK-4631)
|
||||
</li>
|
||||
<li>Ethernet support via RAK-13800</li>
|
||||
<li>
|
||||
Compass improvements for larger screens and
|
||||
customizations
|
||||
</li>
|
||||
</p>
|
||||
<h2>Nerd stuff!</h2>
|
||||
<p className="leading-7 --ifm-font-color-base">
|
||||
<li>New filesystem for ESP32 (LittleFS)</li>
|
||||
<li>
|
||||
Upgraded Arduino framework for both NRF52 and ESP32
|
||||
</li>
|
||||
<li>New bluetooth stack for ESP32 (NimBLE)</li>
|
||||
<li>Unified GPS stack now using NMEA</li>
|
||||
<li>Support for more I2C sensors</li>
|
||||
<li>Support for ATECCA608B Cryptographic Coprocessor</li>
|
||||
<li>More Serial module I/O modes</li>
|
||||
<li>JSON messages over MQTT</li>
|
||||
<li>
|
||||
Device codebase refactored and optimized in many areas
|
||||
</li>
|
||||
<li>
|
||||
Completely restructured protobufs and configuration
|
||||
</li>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats section */}
|
||||
<div className="mt-10">
|
||||
<dl className="grid grid-cols-2 gap-x-4 gap-y-8">
|
||||
{stats.map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="border-t-2 border-gray-100 pt-6"
|
||||
>
|
||||
<dt className="font-medium --ifm-font-color-base">
|
||||
{stat.label}
|
||||
</dt>
|
||||
<dd className="text-3xl font-bold tracking-tight --ifm-heading-color">
|
||||
{stat.value}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Logo cloud section */}
|
||||
<div className="mt-32">
|
||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="lg:grid lg:grid-cols-2 lg:items-center lg:gap-24">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
||||
All made possible by the amazing companies that support us.
|
||||
</h2>
|
||||
<p className="mt-6 max-w-3xl text-lg leading-7 --ifm-font-color-base">
|
||||
Running a project of this scale is no easy feat, without the
|
||||
generosity of many of our vendors and providers, none of
|
||||
this would be possible.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-12 grid grid-cols-2 gap-0.5 md:grid-cols-3 lg:mt-0 lg:grid-cols-2">
|
||||
{logos.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>
|
||||
</div>
|
||||
|
||||
{/* CTA section */}
|
||||
<div className="relative mt-24 sm:mt-32 sm:py-16">
|
||||
<div aria-hidden="true" className="hidden sm:block">
|
||||
<div className="absolute inset-y-0 left-0 w-1/2 rounded-r-3xl bg-gray-50" />
|
||||
<svg
|
||||
className="absolute top-8 left-1/2 -ml-3"
|
||||
width={404}
|
||||
height={392}
|
||||
fill="none"
|
||||
viewBox="0 0 404 392"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="8228f071-bcee-4ec8-905a-2a059a2cc4fb"
|
||||
x={0}
|
||||
y={0}
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={4}
|
||||
height={4}
|
||||
className="text-gray-200"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect
|
||||
width={404}
|
||||
height={392}
|
||||
fill="url(#8228f071-bcee-4ec8-905a-2a059a2cc4fb)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||
<div className="relative overflow-hidden rounded-2xl bg-green-500 px-6 py-10 shadow-xl sm:px-12 sm:py-20">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0 -mt-72 sm:-mt-32 md:mt-0"
|
||||
>
|
||||
<svg
|
||||
className="absolute inset-0 h-full w-full"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 1463 360"
|
||||
>
|
||||
<path
|
||||
className="text-green-400 text-opacity-40"
|
||||
fill="currentColor"
|
||||
d="M-82.673 72l1761.849 472.086-134.327 501.315-1761.85-472.086z"
|
||||
/>
|
||||
<path
|
||||
className="text-green-600 text-opacity-40"
|
||||
fill="currentColor"
|
||||
d="M-217.088 544.086L1544.761 72l134.327 501.316-1761.849 472.086z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col gap-12">
|
||||
<div className="sm:text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||
Congratulations to the winners!
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default TwoPointZero;
|
|
@ -1,44 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,165 +1,112 @@
|
|||
import React from "react";
|
||||
import Layout from "@theme/Layout";
|
||||
import Link from "@docusaurus/Link";
|
||||
|
||||
import { AvatarLayout } from "./_components/Avatar";
|
||||
import Layout from "@theme/Layout";
|
||||
import React from "react";
|
||||
|
||||
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>
|
||||
);
|
||||
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"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Open Collective
|
||||
</a>{" "}
|
||||
and the{" "}
|
||||
<a
|
||||
className="underline"
|
||||
href="https://www.oscollective.org"
|
||||
rel="noreferrer"
|
||||
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"
|
||||
rel="noreferrer"
|
||||
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"
|
||||
rel="noreferrer"
|
||||
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>
|
||||
</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>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Credits;
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -3,149 +3,77 @@ import React from "react";
|
|||
import { DeviceFirmwareResource } from "../../../utils/apiTypes";
|
||||
|
||||
export interface releaseCardProps {
|
||||
variant: string;
|
||||
description: string;
|
||||
release?: DeviceFirmwareResource[];
|
||||
variant: string;
|
||||
description: string;
|
||||
release?: DeviceFirmwareResource[];
|
||||
}
|
||||
|
||||
export const FirmwareCard = ({
|
||||
variant,
|
||||
description,
|
||||
release,
|
||||
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>
|
||||
);
|
||||
return (
|
||||
<div className="card m-4 border-2 border-secondary">
|
||||
<div className="card__header flex justify-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
|
||||
type="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>
|
||||
);
|
||||
return (
|
||||
<div className="card w-full animate-pulse flex gap-4 p-4">
|
||||
<div className="flex justify-between mb-4">
|
||||
<div className="rounded-md bg-gray-500 w-32 h-4" />
|
||||
<div className="rounded-md bg-gray-500 w-32 h-4" />
|
||||
</div>
|
||||
<div className="card__body rounded-md bg-gray-500 h-12" />
|
||||
<a className="button disabled button--primary button--block"> </a>
|
||||
<div className="rounded-md bg-gray-500 w-32 h-8" />
|
||||
<div className="rounded-md bg-gray-500 w-44 h-4" />
|
||||
<div className="rounded-md bg-gray-500 w-36 h-4" />
|
||||
<div className="rounded-md bg-gray-500 w-52 h-4" />
|
||||
<div className="rounded-md bg-gray-500 w-44 h-4" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -4,219 +4,219 @@ import { FaAndroid, FaApple } from "react-icons/fa";
|
|||
import useSWR from "swr";
|
||||
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
BoltIcon,
|
||||
ComputerDesktopIcon,
|
||||
CpuChipIcon,
|
||||
GlobeAltIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
BoltIcon,
|
||||
ComputerDesktopIcon,
|
||||
CpuChipIcon,
|
||||
GlobeAltIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import Layout from "@theme/Layout";
|
||||
|
||||
import { FirmwareReleases } from "../../utils/apiTypes";
|
||||
import { fetcher } from "../../utils/swr";
|
||||
import {
|
||||
FirmwareCard,
|
||||
PlaceholderFirmwareCard,
|
||||
FirmwareCard,
|
||||
PlaceholderFirmwareCard,
|
||||
} from "./_components/FirmwareCard";
|
||||
|
||||
const Firmware = (): JSX.Element => {
|
||||
const { data, error } = useSWR<FirmwareReleases>(
|
||||
"https://api.meshtastic.org/github/firmware/list",
|
||||
fetcher,
|
||||
);
|
||||
const { data, error } = useSWR<FirmwareReleases>(
|
||||
"https://api.meshtastic.org/github/firmware/list",
|
||||
fetcher,
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout
|
||||
title="Downloads"
|
||||
description="Downloads for the Meshtastic project"
|
||||
>
|
||||
<div className="container mt-8 flex flex-col gap-3">
|
||||
<h1 className="m-2">Flasher</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-green-500 to-primary">
|
||||
<BoltIcon className="m-auto h-20" />
|
||||
</div>
|
||||
<div className="flex w-full flex-col bg-primary xl:flex-row">
|
||||
<div className="card m-4 border-2 border-secondary">
|
||||
<div className="card__header">
|
||||
<h3>ESP32 Web Flasher</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Web based installer for easy flashing with Chrome and Edge
|
||||
Browser. Works with T-Beam, T-Lora, Nano-G1 and similar
|
||||
boards.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="https://flasher.meshtastic.org/"
|
||||
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"
|
||||
>
|
||||
Go to Flasher
|
||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card m-4 border-2 border-secondary w-full">
|
||||
<div className="card__header">
|
||||
<h3>nRF52 Drag & Drop</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Devices such as T-Echo and RAK4631 are flashed via filesystem.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="/docs/getting-started/flashing-firmware/nrf52/drag-n-drop"
|
||||
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"
|
||||
>
|
||||
View Instructions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* */}
|
||||
<h1 className="m-2">Apps</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-rose-500 to-primary">
|
||||
<ComputerDesktopIcon className="m-auto h-20" />
|
||||
</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="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1586432531"
|
||||
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="https://meshtastic.org/docs/software/android/installation"
|
||||
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="https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source=downloads-page"
|
||||
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">
|
||||
Requires Chromium based browsers.
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://client.meshtastic.org"
|
||||
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"
|
||||
>
|
||||
client.meshtastic.org
|
||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* */}
|
||||
<h1 className="m-2">Firmware</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-orange-500 to-primary">
|
||||
<CpuChipIcon className="m-auto h-20" />
|
||||
</div>
|
||||
<div className="flex w-full flex-col bg-primary lg:flex-row">
|
||||
{data && !error ? (
|
||||
<>
|
||||
<FirmwareCard
|
||||
variant="Stable"
|
||||
description="Tested feature set. For those who want stability."
|
||||
release={data.releases.stable}
|
||||
/>
|
||||
<FirmwareCard
|
||||
variant="Alpha"
|
||||
description="Upcoming changes for testing. For those who want new features."
|
||||
release={data.releases.alpha}
|
||||
/>
|
||||
<div className="card m-4 border-2 border-secondary">
|
||||
<div className="card__header">
|
||||
<h3>Bleeding</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Latest successful CI build. For those who want to break
|
||||
things.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="https://nightly.link/meshtastic/firmware/workflows/main_matrix/master"
|
||||
className="button button--secondary button--block"
|
||||
>
|
||||
Download Bleeding
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlaceholderFirmwareCard />
|
||||
<PlaceholderFirmwareCard />
|
||||
<PlaceholderFirmwareCard />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
return (
|
||||
<Layout
|
||||
title="Downloads"
|
||||
description="Downloads for the Meshtastic project"
|
||||
>
|
||||
<div className="container mt-8 flex flex-col gap-3">
|
||||
<h1 className="m-2">Flasher</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-green-500 to-primary">
|
||||
<BoltIcon className="m-auto h-20" />
|
||||
</div>
|
||||
<div className="flex w-full flex-col bg-primary xl:flex-row">
|
||||
<div className="card m-4 border-2 border-secondary">
|
||||
<div className="card__header">
|
||||
<h3>ESP32 Web Flasher</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Web based installer for easy flashing with Chrome and Edge
|
||||
Browser. Works with T-Beam, T-Lora, Nano-G1 and similar
|
||||
boards.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="https://flasher.meshtastic.org/"
|
||||
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"
|
||||
>
|
||||
Go to Flasher
|
||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card m-4 border-2 border-secondary w-full">
|
||||
<div className="card__header">
|
||||
<h3>nRF52 Drag & Drop</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Devices such as T-Echo and RAK4631 are flashed via filesystem.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="/docs/getting-started/flashing-firmware/nrf52/drag-n-drop"
|
||||
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"
|
||||
>
|
||||
View Instructions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* */}
|
||||
<h1 className="m-2">Apps</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-rose-500 to-primary">
|
||||
<ComputerDesktopIcon className="m-auto h-20" />
|
||||
</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="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1586432531"
|
||||
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="https://meshtastic.org/docs/software/android/installation"
|
||||
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="https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source=downloads-page"
|
||||
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">
|
||||
Requires Chromium based browsers.
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://client.meshtastic.org"
|
||||
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"
|
||||
>
|
||||
client.meshtastic.org
|
||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* */}
|
||||
<h1 className="m-2">Firmware</h1>
|
||||
<div className="flex w-full overflow-hidden rounded-xl">
|
||||
<div className="flex w-1/5 bg-gradient-to-r from-orange-500 to-primary">
|
||||
<CpuChipIcon className="m-auto h-20" />
|
||||
</div>
|
||||
<div className="flex w-full flex-col bg-primary lg:flex-row">
|
||||
{data && !error ? (
|
||||
<>
|
||||
<FirmwareCard
|
||||
variant="Stable"
|
||||
description="Tested feature set. For those who want stability."
|
||||
release={data.releases.stable}
|
||||
/>
|
||||
<FirmwareCard
|
||||
variant="Alpha"
|
||||
description="Upcoming changes for testing. For those who want new features."
|
||||
release={data.releases.alpha}
|
||||
/>
|
||||
<div className="card m-4 border-2 border-secondary">
|
||||
<div className="card__header">
|
||||
<h3>Bleeding</h3>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<p>
|
||||
Latest successful CI build. For those who want to break
|
||||
things.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__footer mt-auto">
|
||||
<a
|
||||
href="https://nightly.link/meshtastic/firmware/workflows/main_matrix/master"
|
||||
className="button button--secondary button--block"
|
||||
>
|
||||
Download Bleeding
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlaceholderFirmwareCard />
|
||||
<PlaceholderFirmwareCard />
|
||||
<PlaceholderFirmwareCard />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Firmware;
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
import { FiPlus } from "react-icons/fi";
|
||||
|
||||
import { HardwareModal } from "../../components/hardware/HardwareModal";
|
||||
import { IDevice } from "../../data/device";
|
||||
|
||||
import { HardwareCard } from "../../components/hardware/HardwareCard";
|
||||
import { PageLayout } from "../../components/PageLayout";
|
||||
import { heltec } from "../../data/devices/heltec";
|
||||
import { hydra } from "../../data/devices/hydra";
|
||||
import { nano_g1 } from "../../data/devices/nano_g1";
|
||||
import { rak19001 } from "../../data/devices/rak19001";
|
||||
import { rak19003 } from "../../data/devices/rak19003";
|
||||
import { tbeam } from "../../data/devices/tbeam";
|
||||
import { techo } from "../../data/devices/techo";
|
||||
|
||||
const Hardware = (): JSX.Element => {
|
||||
const hardware = [tbeam, hydra, rak19003, rak19001, nano_g1, heltec, techo];
|
||||
const [modalData, setModalData] = useState<IDevice>();
|
||||
|
||||
return (
|
||||
<PageLayout title="Hardware" description="Supported hardware">
|
||||
<div className="border-b border-tertiary p-4">
|
||||
<div className="sm:flex sm:items-baseline">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900">
|
||||
Issues
|
||||
</h3>
|
||||
<div className="mt-4 sm:mt-0 sm:ml-10">
|
||||
<nav className="-mb-px flex space-x-8">
|
||||
<button
|
||||
className="border-indigo-500 text-indigo-600"
|
||||
aria-current={"page"}
|
||||
>
|
||||
Devices
|
||||
</button>
|
||||
<button
|
||||
className="hover:border-gray-300', 'whitespace-nowrap border-b-2 border-transparent
|
||||
px-1 pb-4 text-sm font-medium text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Antennas
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl py-8 px-4 sm:px-6 lg:px-8">
|
||||
<ul
|
||||
role="list"
|
||||
className="grid grid-cols-2 gap-x-2 gap-y-4 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:grid-cols-5 xl:gap-x-4"
|
||||
>
|
||||
{hardware.map((device) => (
|
||||
<HardwareCard
|
||||
key={device.name}
|
||||
device={device}
|
||||
setDevice={(): void => {
|
||||
setModalData(device);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<li className="group relative">
|
||||
<a
|
||||
href="https://github.com/meshtastic/firmware/issues/new?assignees=&labels=enhancement%2Ctriage&template=New+Board.yml&title=%5BBoard%5D%3A+"
|
||||
className="flex aspect-[4/3] rounded-lg border-2 border-dashed border-mute group-hover:border-tertiaryInv"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<FiPlus className="m-auto h-12 w-12 text-mute group-hover:text-tertiaryInv" />
|
||||
</a>
|
||||
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
||||
New Board
|
||||
</p>
|
||||
<p className="pointer-events-none block text-sm font-medium text-mute">
|
||||
Want to support a board?
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{modalData && (
|
||||
<HardwareModal
|
||||
open={!!modalData}
|
||||
close={() => {
|
||||
setModalData(undefined);
|
||||
}}
|
||||
device={modalData}
|
||||
/>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hardware;
|
|
@ -1,9 +1,5 @@
|
|||
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";
|
||||
|
@ -12,311 +8,199 @@ import Layout from "@theme/Layout";
|
|||
|
||||
import { SocialCard, SocialCardProps } from "../components/homepage/SocialCard";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: "Radio Mesh Text Messaging",
|
||||
imageUrl: "img/homepage/messages.svg",
|
||||
description: (
|
||||
<>
|
||||
Off-grid messaging using inexpensive hardware to create your personal
|
||||
mesh. Radios forward messages to the next to flood the network.
|
||||
Communicate kilometers/miles between nodes. Internet-connected relay
|
||||
nodes enable the conversation to move online too.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Encryption",
|
||||
imageUrl: "img/homepage/encryption.svg",
|
||||
description: (
|
||||
<>
|
||||
Messages are AES256 encrypted. Only radios supplied with your channel
|
||||
settings (which includes the key) should be able to read your messages.
|
||||
Using multichannel settings you can send encrypted messages on one
|
||||
channel and still participate in a default Meshtastic mesh.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Conserve Battery",
|
||||
imageUrl: "img/homepage/battery.svg",
|
||||
description: (
|
||||
<>
|
||||
Go for days on end and on a single battery or extend it infinitely with
|
||||
a solar cell. Power management ensures the device will last the duration
|
||||
of your use.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Extensible",
|
||||
imageUrl: "img/homepage/extendable.svg",
|
||||
description: (
|
||||
<>
|
||||
Create a highly scalable mesh with hardware on a multitude of platforms
|
||||
to fit your unique requirements: Create an environment monitoring mesh
|
||||
and produce real-time heatmaps, or maybe decentralized, encrypted
|
||||
messaging network, your imagination is the limit.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Platform Agnostic",
|
||||
imageUrl: "img/homepage/platforms.svg",
|
||||
description: (
|
||||
<>
|
||||
Meshtastic clients are built or being built for all major desktop and
|
||||
mobile platforms. Linux, Windows, Mac, Android, and iOS are all
|
||||
supported or well on their way to being supported.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Open Source",
|
||||
imageUrl: "img/homepage/opensource.svg",
|
||||
description: (
|
||||
<>
|
||||
All Meshtastic software is open source. If you want an improvement,
|
||||
submit a pull request or file an issue on Github. Happy coding!
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const SocialCards: SocialCardProps[] = [
|
||||
{
|
||||
color: "bg-[#5865F2]",
|
||||
link: "https://discord.com/invite/ktMAKGBnBs",
|
||||
children: (
|
||||
<img
|
||||
alt="discord"
|
||||
className="m-auto h-10"
|
||||
src="/img/homepage/Discord-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#4A99E9]",
|
||||
link: "https://twitter.com/TheMeshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="twitter"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/Twitter-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#3875EA]",
|
||||
link: "https://facebook.com/themeshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="facebook"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/f_logo_RGB-White_1024.png"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#ffffff]",
|
||||
link: "https://www.instagram.com/themeshtastic/",
|
||||
children: (
|
||||
<img
|
||||
alt="instagram"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/Instagram_Glyph_Gradient.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#FF0000]",
|
||||
link: "https://www.youtube.com/meshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="youtube"
|
||||
className="m-auto h-16"
|
||||
src="/img/homepage/YouTube-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#ffffff]",
|
||||
link: "https://meshtastic.discourse.group",
|
||||
children: (
|
||||
<img
|
||||
alt="discourse"
|
||||
className="m-auto h-12"
|
||||
src="/img/homepage/Discourse-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#FF4500]",
|
||||
link: "https://reddit.com/r/meshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="reddit"
|
||||
className="m-auto h-20"
|
||||
src="/img/homepage/Reddit-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#5865F2]",
|
||||
link: "https://discord.com/invite/ktMAKGBnBs",
|
||||
children: (
|
||||
<img
|
||||
alt="discord"
|
||||
className="m-auto h-10"
|
||||
src="/img/homepage/Discord-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#4A99E9]",
|
||||
link: "https://twitter.com/TheMeshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="twitter"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/Twitter-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#3875EA]",
|
||||
link: "https://facebook.com/themeshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="facebook"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/f_logo_RGB-White_1024.png"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#ffffff]",
|
||||
link: "https://www.instagram.com/themeshtastic/",
|
||||
children: (
|
||||
<img
|
||||
alt="instagram"
|
||||
className="m-auto h-14"
|
||||
src="/img/homepage/Instagram_Glyph_Gradient.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#FF0000]",
|
||||
link: "https://www.youtube.com/meshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="youtube"
|
||||
className="m-auto h-16"
|
||||
src="/img/homepage/YouTube-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#ffffff]",
|
||||
link: "https://meshtastic.discourse.group",
|
||||
children: (
|
||||
<img
|
||||
alt="discourse"
|
||||
className="m-auto h-12"
|
||||
src="/img/homepage/Discourse-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: "bg-[#FF4500]",
|
||||
link: "https://reddit.com/r/meshtastic",
|
||||
children: (
|
||||
<img
|
||||
alt="reddit"
|
||||
className="m-auto h-20"
|
||||
src="/img/homepage/Reddit-Logo-White.svg"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Home() {
|
||||
const context = useDocusaurusContext();
|
||||
const { siteConfig } = context;
|
||||
return (
|
||||
<Layout>
|
||||
<Head>
|
||||
<meta property="og:title" content="Meshtastic" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content={useBaseUrl("design/web/social-preview-1200x630.png")}
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices"
|
||||
/>
|
||||
<meta property="og:url" content="https://meshtastic.org/" />
|
||||
<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="Meshtastic Logo"
|
||||
className="header__logo"
|
||||
src={useBaseUrl("design/typelogo/typelogo.svg")}
|
||||
/>
|
||||
</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className="indexCtas">
|
||||
<Link className="button button--lg" to="/docs/introduction">
|
||||
Learn More
|
||||
</Link>
|
||||
<Link className="button button--lg" to="/docs/getting-started">
|
||||
Get 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>
|
||||
const context = useDocusaurusContext();
|
||||
const { siteConfig } = context;
|
||||
return (
|
||||
<Layout>
|
||||
<Head>
|
||||
<meta property="og:title" content="Meshtastic" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content={useBaseUrl("design/web/social-preview-1200x630.png")}
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices"
|
||||
/>
|
||||
<meta property="og:url" content="https://meshtastic.org/" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</Head>
|
||||
<header className="hero hero--primary text-center">
|
||||
<div className="container">
|
||||
<h1 className="hero__title">
|
||||
<img
|
||||
alt="Meshtastic Logo"
|
||||
className="header__logo py-8"
|
||||
src={useBaseUrl("design/typelogo/typelogo.svg")}
|
||||
/>
|
||||
</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className="indexCtas">
|
||||
<Link className="button button--lg" to="/docs/introduction">
|
||||
Learn More
|
||||
</Link>
|
||||
<Link className="button button--lg" to="/docs/getting-started">
|
||||
Get Started
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex flex-col gap-4">
|
||||
<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">Connect with us.</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="bg-primaryDark mx-auto flex w-full lg:w-auto flex-col gap-4 p-4 shadow-inner">
|
||||
<h3 className="text-xl font-bold">Connect with us.</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">
|
||||
Getting started with Meshtastic is as easy as 1, 2, 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. Purchase Supported Hardware</h3>
|
||||
</div>
|
||||
<div
|
||||
className="card__body"
|
||||
style={{ display: "flex", justifyContent: "center" }}
|
||||
>
|
||||
<p>
|
||||
Hardware you will want to consider:
|
||||
<ul>
|
||||
<li>Radio</li>
|
||||
<li>Battery</li>
|
||||
<li>Case</li>
|
||||
<li>
|
||||
Antenna (most devices include an antenna, but the quality
|
||||
can be a bit of a mixed bag from some suppliers on stock
|
||||
antennas)
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div
|
||||
className="card__header"
|
||||
style={{ display: "flex", justifyContent: "space-between" }}
|
||||
>
|
||||
<h3>2. Flash & Configure Node</h3>
|
||||
</div>
|
||||
<div
|
||||
className="card__body"
|
||||
style={{ display: "flex", justifyContent: "center" }}
|
||||
>
|
||||
<p>
|
||||
The Meshtastic Web-Based Flasher & Clients can assist you in
|
||||
flashing the firmware and configuring settings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div
|
||||
className="card__header"
|
||||
style={{ display: "flex", justifyContent: "space-between" }}
|
||||
>
|
||||
<h3>3. Connect to Node</h3>
|
||||
</div>
|
||||
<div
|
||||
className="card__body"
|
||||
style={{ display: "flex", justifyContent: "center" }}
|
||||
>
|
||||
<p>
|
||||
Applications are available for the following systems:
|
||||
<ul>
|
||||
<li>Android</li>
|
||||
<li>iOS</li>
|
||||
<li>Mac</li>
|
||||
<li>Web Browser</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
<div className="container mx-auto flex w-auto flex-col">
|
||||
<h2 className="mb-2 text-xl font-medium">
|
||||
Getting started with Meshtastic is as easy as 1, 2, 3!
|
||||
</h2>
|
||||
<ul
|
||||
className="mx-auto relative grid gap-6"
|
||||
style={{
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
|
||||
}}
|
||||
>
|
||||
<div className="card">
|
||||
<div className="card__header flex justify-between">
|
||||
<h3>1. Purchase Supported Hardware</h3>
|
||||
</div>
|
||||
<div className="card__body flex justify-center">
|
||||
<p>
|
||||
Hardware you will want to consider:
|
||||
<ul>
|
||||
<li>Radio</li>
|
||||
<li>Battery</li>
|
||||
<li>Case</li>
|
||||
<li>
|
||||
Antenna (most devices include an antenna, but the quality
|
||||
can be a bit of a mixed bag from some suppliers on stock
|
||||
antennas)
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div className="card__header flex justify-between">
|
||||
<h3>2. Flash & Configure Node</h3>
|
||||
</div>
|
||||
<div className="card__body flex justify-center">
|
||||
<p>
|
||||
The Meshtastic Web-Based Flasher & Clients can assist you in
|
||||
flashing the firmware and configuring settings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div className="card__header flex justify-between">
|
||||
<h3>3. Connect to Node</h3>
|
||||
</div>
|
||||
<div className="card__body flex justify-center">
|
||||
<p>
|
||||
Applications are available for the following systems:
|
||||
<ul>
|
||||
<li>Android</li>
|
||||
<li>iOS</li>
|
||||
<li>Mac</li>
|
||||
<li>Web Browser</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
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>
|
||||
);
|
|
@ -1,29 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,90 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,318 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,67 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,52 +0,0 @@
|
|||
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>
|
||||
);
|
||||
};
|
|
@ -1,111 +0,0 @@
|
|||
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>
|
||||
);
|
|
@ -1,35 +0,0 @@
|
|||
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 Meshtastic community"
|
||||
>
|
||||
<main className="margin-vert--lg">
|
||||
{id ? (
|
||||
<Network id={id} />
|
||||
) : (
|
||||
<>
|
||||
<Filters />
|
||||
<Networks />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Showcase;
|
|
@ -6,146 +6,148 @@ 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());
|
||||
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]);
|
||||
useEffect(() => {
|
||||
setOemBytes(
|
||||
new Protobuf.OEMStore({
|
||||
oemAesKey,
|
||||
oemFont,
|
||||
oemIconBits,
|
||||
oemIconHeight,
|
||||
oemIconWidth,
|
||||
oemText,
|
||||
}).toBinary(),
|
||||
);
|
||||
}, [oemAesKey, oemFont, oemIconBits, oemIconHeight, oemIconWidth, oemText]);
|
||||
|
||||
const enumOptions = Protobuf.ScreenFonts
|
||||
? Object.entries(Protobuf.ScreenFonts).filter(
|
||||
(value) => typeof value[1] === "number",
|
||||
)
|
||||
: [];
|
||||
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();
|
||||
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.onload = (res) => {
|
||||
resolve(res.target.result as string);
|
||||
};
|
||||
reader.onerror = (err) => reject(err);
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
};
|
||||
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>
|
||||
);
|
||||
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
|
||||
type="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
|
||||
type="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;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Translate, { translate } from "@docusaurus/Translate";
|
||||
import { PageMetadata } from "@docusaurus/theme-common";
|
||||
import Layout from "@theme/Layout";
|
||||
import React from "react";
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<>
|
||||
|
@ -15,7 +15,7 @@ export default function NotFound() {
|
|||
<main className="container margin-vert--xl">
|
||||
<div className="row">
|
||||
<div className="col col--6 col--offset-3">
|
||||
<h1 className="hero__title">
|
||||
<h1 className="hero__title text-center mb-8">
|
||||
<Translate
|
||||
id="theme.NotFound.title"
|
||||
description="The title of the 404 page"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const BREAKPOINTS = { sm: 640, md: 768, lg: 1024, xl: 1280 };
|
|
@ -1,13 +0,0 @@
|
|||
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}`;
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
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 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 651.29 94.76"><path d="m143.05 93.42 1.07-3.71c1.27-4.41.8-8.48-1.34-11.48-2-2.76-5.26-4.38-9.25-4.57L58 72.7a1.47 1.47 0 0 1-1.35-2 2 2 0 0 1 1.75-1.34l76.26-1c9-.41 18.84-7.75 22.27-16.71l4.34-11.36a2.68 2.68 0 0 0 .18-1 3.31 3.31 0 0 0-.06-.54 49.67 49.67 0 0 0-95.49-5.14 22.35 22.35 0 0 0-35 23.42A31.73 31.73 0 0 0 .34 93.45a1.47 1.47 0 0 0 1.45 1.27h139.49a1.83 1.83 0 0 0 1.77-1.3Z" style="fill:#f78100"/><path d="M168.22 41.15q-1 0-2.1.06a.88.88 0 0 0-.32.07 1.17 1.17 0 0 0-.76.8l-3 10.26c-1.28 4.41-.81 8.48 1.34 11.48a11.65 11.65 0 0 0 9.24 4.57l16.11 1a1.44 1.44 0 0 1 1.14.62 1.5 1.5 0 0 1 .17 1.37 2 2 0 0 1-1.75 1.34l-16.73 1c-9.09.42-18.88 7.75-22.31 16.7l-1.21 3.16a.9.9 0 0 0 .79 1.22h57.63a1.55 1.55 0 0 0 1.54-1.17 41.34 41.34 0 0 0-39.76-52.48Z" style="fill:#fcad32"/><path d="M273.03 59.66h9.53v26.06h16.67v8.35h-26.2V59.66zM309.11 77v-.09c0-9.88 8-17.9 18.58-17.9s18.48 7.92 18.48 17.8v.1c0 9.88-8 17.89-18.58 17.89s-18.48-7.95-18.48-17.8m27.33 0v-.09c0-5-3.59-9.29-8.85-9.29s-8.7 4.22-8.7 9.19v.1c0 5 3.59 9.29 8.8 9.29s8.75-4.23 8.75-9.2m21.4 2V59.66h9.69v19.12c0 5 2.5 7.33 6.34 7.33s6.34-2.26 6.34-7.08V59.66h9.68v19.07c0 11.11-6.34 16-16.12 16s-15.93-5-15.93-15.73m46.65-19.34h13.27c12.29 0 19.42 7.08 19.42 17v.1c0 9.93-7.23 17.3-19.61 17.3h-13.08Zm13.42 26c5.7 0 9.49-3.15 9.49-8.71v-.09c0-5.51-3.79-8.71-9.49-8.71H414v17.47Zm33.13-26h27.52v8.36h-17.98v5.85h16.27v7.91h-16.27v12.29h-9.54V59.66zm40.8 0h9.53v26.06h16.67v8.35h-26.2V59.66zm51.16-.24h9.19l14.61 34.65h-10.22l-2.51-6.14h-13.28l-2.45 6.14h-10Zm8.35 21.08-3.83-9.78-3.92 9.78Zm27.73-20.84h16.27c5.27 0 8.9 1.38 11.21 3.74a10.64 10.64 0 0 1 3.05 8v.1a10.88 10.88 0 0 1-7.08 10.57l8.21 12h-11l-6.94-10.42h-4.18v10.42h-9.54Zm15.83 16.52c3.25 0 5.12-1.58 5.12-4.08V72c0-2.71-2-4.08-5.17-4.08h-6.24v8.26Zm28.46-16.52h27.68v8.11h-18.24v5.21h16.52v7.52h-16.52v5.46h18.48v8.11h-27.92V59.66zM252.15 81a8.44 8.44 0 0 1-7.88 5.16c-5.22 0-8.8-4.33-8.8-9.29v-.1c0-5 3.49-9.2 8.7-9.2a8.64 8.64 0 0 1 8.18 5.71h10C260.79 65.09 253.6 59 244.27 59c-10.62 0-18.58 8-18.58 17.9v.1c0 9.88 7.86 17.8 18.48 17.8 9.08 0 16.18-5.88 18.05-13.76Z"/></svg>
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 347 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 988 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="292" height="80" fill="none" viewBox="0 0 292 80"><g clip-path="url(#a)"><g fill="#5865F2" clip-path="url(#b)"><path d="M61.796 16.494a59.415 59.415 0 0 0-15.05-4.73 44.128 44.128 0 0 0-1.928 4.003c-5.612-.844-11.172-.844-16.68 0a42.783 42.783 0 0 0-1.95-4.002 59.218 59.218 0 0 0-15.062 4.74C1.6 30.9-.981 44.936.31 58.772c6.317 4.717 12.44 7.583 18.458 9.458a45.906 45.906 0 0 0 3.953-6.51 38.872 38.872 0 0 1-6.225-3.03 30.957 30.957 0 0 0 1.526-1.208c12.004 5.615 25.046 5.615 36.906 0 .499.416 1.01.82 1.526 1.208a38.775 38.775 0 0 1-6.237 3.035 45.704 45.704 0 0 0 3.953 6.511c6.025-1.875 12.153-4.74 18.47-9.464 1.515-16.04-2.588-29.947-10.844-42.277Zm-37.44 33.767c-3.603 0-6.558-3.363-6.558-7.46 0-4.096 2.892-7.466 6.559-7.466 3.666 0 6.621 3.364 6.558 7.466.006 4.097-2.892 7.46-6.558 7.46Zm24.237 0c-3.603 0-6.558-3.363-6.558-7.46 0-4.096 2.892-7.466 6.558-7.466 3.667 0 6.622 3.364 6.558 7.466 0 4.097-2.891 7.46-6.558 7.46ZM98.03 26.17h15.663c3.776 0 6.966.604 9.583 1.806 2.61 1.201 4.567 2.877 5.864 5.022 1.296 2.145 1.95 4.6 1.95 7.367 0 2.707-.677 5.163-2.031 7.36-1.354 2.204-3.414 3.944-6.185 5.228-2.771 1.283-6.203 1.928-10.305 1.928h-14.54V26.17Zm14.378 21.414c2.542 0 4.499-.65 5.864-1.945 1.366-1.301 2.049-3.071 2.049-5.316 0-2.08-.609-3.739-1.825-4.98-1.216-1.243-3.058-1.87-5.52-1.87h-4.9v14.111h4.332Zm42.133 7.262c-2.169-.575-4.126-1.407-5.864-2.503v-6.81c1.314 1.038 3.075 1.893 5.284 2.567 2.209.668 4.344 1.002 6.409 1.002.964 0 1.693-.128 2.186-.386.494-.258.741-.569.741-.926 0-.41-.132-.75-.402-1.026-.27-.275-.792-.504-1.566-.697l-4.82-1.108c-2.76-.656-4.717-1.565-5.881-2.73-1.165-1.161-1.745-2.685-1.745-4.572 0-1.588.505-2.965 1.527-4.143 1.015-1.178 2.461-2.087 4.337-2.725 1.877-.645 4.068-.967 6.587-.967 2.249 0 4.309.246 6.186.738 1.876.492 3.425 1.12 4.659 1.887v6.44c-1.263-.767-2.709-1.37-4.361-1.828a19.138 19.138 0 0 0-5.084-.674c-2.519 0-3.775.44-3.775 1.313 0 .41.195.715.585.92.39.205 1.107.416 2.146.639l4.016.738c2.623.463 4.579 1.278 5.864 2.438 1.286 1.16 1.928 2.878 1.928 5.152 0 2.49-1.061 4.465-3.19 5.93-2.129 1.465-5.147 2.198-9.06 2.198a26.36 26.36 0 0 1-6.707-.867Zm28.437-.862c-2.3-1.149-4.039-2.708-5.198-4.677-1.159-1.969-1.744-4.184-1.744-6.645 0-2.462.602-4.665 1.807-6.605 1.205-1.94 2.972-3.464 5.302-4.571 2.329-1.108 5.112-1.659 8.354-1.659 4.016 0 7.35.862 10.001 2.585v7.507c-.935-.656-2.026-1.19-3.271-1.6-1.245-.41-2.576-.615-3.999-.615-2.49 0-4.435.463-5.841 1.395-1.406.931-2.111 2.144-2.111 3.65 0 1.477.682 2.685 2.048 3.634 1.366.944 3.345 1.418 5.944 1.418 1.337 0 2.657-.2 3.959-.592 1.297-.398 2.416-.885 3.351-1.459v7.261c-2.943 1.805-6.357 2.707-10.242 2.707-3.27-.011-6.059-.586-8.36-1.734Zm28.54 0c-2.318-1.148-4.085-2.72-5.302-4.718-1.216-1.998-1.83-4.225-1.83-6.686 0-2.462.608-4.66 1.83-6.587 1.222-1.928 2.978-3.44 5.285-4.536 2.3-1.096 5.049-1.641 8.233-1.641 3.185 0 5.933.545 8.234 1.64 2.301 1.097 4.057 2.597 5.262 4.513 1.205 1.917 1.807 4.114 1.807 6.605 0 2.461-.602 4.688-1.807 6.687-1.205 1.998-2.967 3.569-5.285 4.717-2.318 1.149-5.055 1.723-8.216 1.723-3.162 0-5.899-.568-8.211-1.717Zm12.204-7.279c.976-.996 1.469-2.314 1.469-3.955s-.488-2.948-1.469-3.915c-.975-.973-2.307-1.46-3.993-1.46-1.716 0-3.059.487-4.04 1.46-.975.973-1.463 2.274-1.463 3.915 0 1.64.488 2.96 1.463 3.956.976.996 2.324 1.5 4.04 1.5 1.686-.006 3.018-.504 3.993-1.5ZM259.17 31.34v8.86c-1.021-.685-2.341-1.025-3.976-1.025-2.141 0-3.793.662-4.941 1.986-1.153 1.325-1.727 3.388-1.727 6.177v7.548h-9.84V30.888h9.64v7.63c.533-2.79 1.4-4.846 2.593-6.176 1.188-1.325 2.725-1.987 4.596-1.987 1.417 0 2.634.328 3.655.985Zm32.694-5.99v29.537h-9.841v-5.374c-.832 2.022-2.094 3.563-3.792 4.618-1.699 1.049-3.799 1.576-6.289 1.576-2.226 0-4.165-.55-5.824-1.658-1.658-1.108-2.937-2.626-3.838-4.554-.895-1.928-1.349-4.108-1.349-6.546-.028-2.514.448-4.77 1.429-6.769.976-1.998 2.358-3.557 4.137-4.676 1.779-1.12 3.81-1.682 6.088-1.682 4.688 0 7.832 2.08 9.438 6.235V25.35h9.841Zm-11.309 21.191c1.004-.996 1.503-2.29 1.503-3.873 0-1.53-.488-2.778-1.463-3.733-.976-.956-2.313-1.436-3.994-1.436-1.658 0-2.983.486-3.976 1.46-.993.972-1.486 2.232-1.486 3.79 0 1.56.493 2.831 1.486 3.816.993.984 2.301 1.477 3.936 1.477 1.658-.006 2.989-.504 3.994-1.5ZM139.382 33.443c2.709 0 4.906-2.015 4.906-4.5 0-2.486-2.197-4.501-4.906-4.501-2.71 0-4.906 2.015-4.906 4.5 0 2.486 2.196 4.501 4.906 4.501Zm-4.91 3.101c3.006 1.324 6.736 1.383 9.811 0v18.471h-9.811V36.544Z"/></g></g><defs><clipPath id="a"><path fill="#fff" d="M0 11.765h292v56.47H0z"/></clipPath><clipPath id="b"><path fill="#fff" d="M0 11.765h292v56.47H0z"/></clipPath></defs></svg>
|
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 1.2 MiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="457" height="60" viewBox="0 0 457 60"><g fill="none" fill-rule="evenodd"><path fill="#7FADF2" d="M34.62 18.179C36.75 21.249 38 24.979 38 29s-1.25 7.75-3.38 10.821L29.697 34.9a12.16 12.16 0 0 0 1.517-5.9c0-2.14-.55-4.15-1.516-5.9l4.921-4.921Zm-4.8-4.799-4.92 4.922a12.16 12.16 0 0 0-5.9-1.516c-6.746 0-12.214 5.468-12.214 12.214S12.254 41.214 19 41.214c2.14 0 4.15-.55 5.9-1.516l4.921 4.921A18.912 18.912 0 0 1 19 48C8.507 48 0 39.493 0 29s8.507-19 19-19c4.021 0 7.75 1.25 10.821 3.38Z"/><path fill="#B8D3F4" d="M34.62 18.179C36.75 21.249 38 24.979 38 29s-1.25 7.75-3.38 10.821L29.697 34.9a12.16 12.16 0 0 0 1.517-5.9c0-2.14-.55-4.15-1.516-5.9l4.921-4.921Z"/><path fill="#515F71" d="M110.7 13.6c-3.7 0-7.2 1.4-9.8 3.6v-2.3c0-.6-.5-1-1-1h-3.4c-.6 0-1 .5-1 1v43.7c0 .5.4 1 1 1h3.5c.5 0 1-.4 1-1V40.3c2.6 2.2 6.1 3.6 9.8 3.6 8.4 0 15.2-6.8 15.2-15.2-.2-8.4-7-15.1-15.3-15.1Zm0 24.8c-5.4 0-9.7-4.4-9.7-9.7 0-5.4 4.4-9.7 9.7-9.7 5.4 0 9.7 4.4 9.7 9.7 0 5.4-4.4 9.7-9.7 9.7Zm65.9-24.8c-3.7 0-7.1 1.3-9.7 3.5v-1.3c0-1.1-.9-2-2-2h-1.5c-1.1 0-2 .9-2 2v26.3c0 .8.7 1.5 1.5 1.5h2.5c.8 0 1.5-.7 1.5-1.5V29.6c0-5.4 4.4-10.6 9.7-10.6 3.5 0 9.7 1.9 9.7 10.6v12.5c0 .8.7 1.5 1.5 1.5h2.5c.8 0 1.5-.7 1.5-1.5V29.5c0-13.2-10.4-15.9-15.2-15.9Zm-18.4 11.9c-1.5-7.1-8-12.3-15.6-11.9-7.6.4-13.8 6.4-14.3 14-.6 8.9 6.4 16.3 15.1 16.3 5.9 0 11-3.3 13.5-8.2.4-.7-.1-1.6-.9-1.8l-3-.6c-.5-.1-1 .1-1.3.6-1.7 2.7-4.8 4.6-8.2 4.6-3 0-5.7-1.4-7.5-3.5l17.2-7.2 5-2.3Zm-24.4 4.4c0-.4-.1-.8-.1-1.2 0-5.4 4.4-9.7 9.7-9.7 3 0 5.8 1.4 7.5 3.6l-17.1 7.3ZM77.3 13.4c-8.4 0-15.2 6.8-15.2 15.2 0 8.4 6.8 15.2 15.2 15.2 8.4 0 15.2-6.8 15.2-15.2 0-8.4-6.8-15.2-15.2-15.2Zm0 24.9c-5.4 0-9.7-4.4-9.7-9.7 0-5.4 4.4-9.7 9.7-9.7 5.4 0 9.7 4.4 9.7 9.7.1 5.3-4.3 9.7-9.7 9.7Zm181.4-25c-8.4 0-15.2 6.8-15.2 15.2 0 8.4 6.8 15.2 15.2 15.2 8.4 0 15.2-6.8 15.2-15.2 0-8.4-6.8-15.2-15.2-15.2Zm0 24.9c-5.4 0-9.7-4.4-9.7-9.7 0-5.4 4.4-9.7 9.7-9.7 5.4 0 9.7 4.4 9.7 9.7 0 5.3-4.3 9.7-9.7 9.7ZM390.6 4.3h-3.1c-.7 0-1.2.5-1.2 1.2v3.1c0 .7.5 1.2 1.2 1.2h3.1c.7 0 1.2-.5 1.2-1.2V5.5c0-.7-.6-1.2-1.2-1.2Zm63.9 29.4-3.6-.7c-.4-.1-.8.1-1 .5-1.7 2.8-4.8 4.7-8.3 4.7-3 0-5.7-1.4-7.5-3.5l17.2-7.2 5.2-2.2c-1.5-7.1-8-12.3-15.6-11.9-7.6.4-13.8 6.5-14.3 14.1-.6 8.9 6.4 16.3 15.1 16.3 6 0 11.2-3.5 13.6-8.5.2-.8-.2-1.5-.8-1.6Zm-22.7-5.3c0-5.4 4.4-9.7 9.7-9.7 3 0 5.8 1.4 7.5 3.6l-17.2 7.3v-1.2ZM332 25.2c-1.5-7.1-8-12.3-15.6-11.9-7.6.4-13.8 6.5-14.3 14.1-.6 8.9 6.4 16.3 15.1 16.3 6 0 11.2-3.5 13.7-8.6.3-.6-.1-1.3-.7-1.4l-3.6-.7c-.4-.1-.8.1-1 .5-1.7 2.8-4.8 4.7-8.3 4.7-3 0-5.7-1.4-7.5-3.5l17.2-7.2 5-2.3Zm-24.5 4.4c0-.4-.1-.8-.1-1.2 0-5.4 4.4-9.7 9.7-9.7 3 0 5.8 1.4 7.5 3.6l-17.1 7.3ZM283.2.7h-3.6c-.5 0-.9.4-.9.9v40.7c0 .5.4 1 1 1h3.5c.5 0 1-.4 1-1V1.6c0-.5-.5-.9-1-.9Zm12.3 0h-3.6c-.5 0-.9.4-.9.9v40.7c0 .5.4 1 1 1h3.5c.5 0 1-.4 1-1V1.6c-.1-.5-.5-.9-1-.9Zm95.1 12.7h-3.1c-.7 0-1.2.5-1.2 1.2v27.6c0 .7.5 1.2 1.2 1.2h3.1c.7 0 1.2-.5 1.2-1.2V14.5c0-.6-.6-1.1-1.2-1.1Zm-10.2 0h-3.9c-.4 0-.8-.4-.8-.8V1.5c0-.4-.4-.8-.8-.8H371c-.4 0-.8.4-.8.8v11.1c0 .4-.4.8-.8.8h-3.9c-.4 0-.8.4-.8.8V18c0 .4.4.8.8.8h3.9c.4 0 .8.4.8.8v22.7c0 .6.5 1 1 1h3.4c.6 0 1-.5 1-1V19.6c0-.4.4-.8.8-.8h3.9c.4 0 .8-.4.8-.8v-3.9c.1-.4-.2-.7-.7-.7Zm-21.6 22.1c-.4-.4-1.1-.4-1.5 0-1.8 1.7-4.1 2.7-6.8 2.7-5.7 0-10.3-5-9.7-10.8.5-4.5 4.2-8.1 8.7-8.6 2.8-.3 5.5.7 7.4 2.4.6.5 1.5.5 2.1-.1l1.7-1.7c.6-.6.6-1.6 0-2.2-2.9-2.7-6.9-4.2-11.2-3.9-7.7.5-13.9 6.9-14.2 14.7-.3 8.6 6.6 15.6 15.2 15.6 4.1 0 7.9-1.6 10.6-4.3.4-.4.4-1.1 0-1.5l-2.3-2.3Zm-119.7 0c-.4-.4-1.1-.4-1.5 0-1.8 1.7-4.1 2.7-6.8 2.7-5.7 0-10.3-5-9.7-10.8.5-4.5 4.2-8.1 8.7-8.6 2.8-.3 5.5.7 7.4 2.4.6.5 1.5.5 2.1-.1l1.7-1.7c.6-.6.6-1.6 0-2.2-2.9-2.7-6.9-4.2-11.2-3.9-7.7.5-13.9 6.9-14.2 14.7-.3 8.6 6.6 15.6 15.2 15.6 4.1 0 7.9-1.6 10.6-4.3.4-.4.4-1.1 0-1.5l-2.3-2.3ZM424 13.7h-4.9c-.1 0-.2.1-.2.2l-8 23.5c-.1.2-.4.2-.4 0l-8-23.5c0-.1-.1-.2-.2-.2h-4.9c-.2 0-.3.2-.2.3l9.9 29.1c0 .1.1.2.2.2h6.8c.1 0 .2-.1.2-.2l9.9-29.1c.1-.1 0-.3-.2-.3Z"/></g></svg>
|
Before Width: | Height: | Size: 3.9 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.5" viewBox="0 0 1115 116"><path d="m81.582 577.266 66.954-98.462" style="fill:none;stroke:#fff;stroke-width:14.11px" transform="translate(-75.985 -483.202) scale(1.02332)"/><path d="m81.582 577.266 66.954-98.462" style="fill:none;stroke:#fff;stroke-width:14.86px" transform="matrix(1.01019 0 0 1.0102 852.95 -475.627)"/><text x="640.988" y="399.072" style="fill:#fff;font-family:"ArialMT","Arial",sans-serif;font-size:12px" transform="matrix(12.6568 0 0 12.68749 -7898.028 -4950.898)">ESHT</text><path d="m187.032 410.85 63.864-93.658 64.011 93.51" style="fill:none;stroke:#fff;stroke-width:13.85px" transform="translate(-120.199 -314.73) scale(1.02696)"/><path d="m187.032 410.85 63.864-93.658 64.011 93.51" style="fill:none;stroke:#fff;stroke-width:13.85px" transform="translate(417.633 -313.199) scale(1.02696)"/><text style="fill:#fff;font-family:"ArialMT","Arial",sans-serif;font-size:252.715px" transform="matrix(.601 0 0 .60246 750.004 112.652)">ST</text><text style="fill:#fff;font-family:"ArialMT","Arial",sans-serif;font-size:252.715px" transform="matrix(.601 0 0 .60246 1008.532 112.998)">C</text></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="4438" height="1000" fill="none" viewBox="0 0 4438 1000"><path fill="#000" d="M2223.75 250c-172.5 0-296.88 112.5-296.88 281.25s139.85 281.25 312.51 281.25c104.21 0 196.09-41.25 252.96-110.781l-119.53-69.063c-31.56 34.532-79.53 54.688-133.43 54.688-74.85 0-138.44-39.063-162.04-101.563h437.82c3.43-17.5 5.47-35.625 5.47-54.687C2520.63 362.5 2396.41 250 2223.75 250Zm-147.66 226.562C2095.62 414.219 2149.06 375 2223.75 375c74.84 0 128.28 39.219 147.66 101.562h-295.32Zm-35.31-398.437-432.97 750-433.12-750h162.34l270.63 468.75 270.62-468.75h162.5ZM577.344 0l577.346 1000H0L577.344 0ZM3148.75 531.25C3148.75 625 3210 687.5 3305 687.5c64.38 0 112.66-29.219 137.5-76.875l120 69.219C3512.81 762.656 3419.69 812.5 3305 812.5c-172.66 0-296.87-112.5-296.87-281.25S3132.5 250 3305 250c114.69 0 207.66 49.844 257.5 132.656l-120 69.219C3417.66 404.219 3369.38 375 3305 375c-94.84 0-156.25 62.5-156.25 156.25ZM4437.5 78.125v718.75h-140.62V78.125h140.62ZM3906.25 250c-172.5 0-296.87 112.5-296.87 281.25s140 281.25 312.5 281.25c104.21 0 196.09-41.25 252.96-110.781l-119.53-69.063c-31.56 34.532-79.53 54.688-133.43 54.688-74.85 0-138.44-39.063-162.04-101.563h437.82c3.43-17.5 5.46-35.625 5.46-54.687C4203.12 362.5 4078.91 250 3906.25 250Zm-147.66 226.562C3778.13 414.219 3831.41 375 3906.25 375s128.28 39.219 147.66 101.562h-295.32Zm-797.34-210.937v151.406c-15.62-4.531-32.19-7.656-50-7.656-90.78 0-156.25 62.5-156.25 156.25v231.25h-140.62v-531.25H2755v143.75c0-79.375 92.34-143.75 206.25-143.75Z"/></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 410 KiB |
|
@ -1,5 +1,4 @@
|
|||
import type { Config } from "tailwindcss";
|
||||
import typography from "@tailwindcss/typography";
|
||||
|
||||
export default {
|
||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||
|
@ -19,5 +18,5 @@ export default {
|
|||
},
|
||||
},
|
||||
},
|
||||
plugins: [typography()],
|
||||
plugins: [],
|
||||
} satisfies Config;
|
||||
|
|