Merge branch 'master' into range-test-tabs
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
134
docs/configuration/module-config/detection-sensor.mdx
Normal file
|
@ -0,0 +1,134 @@
|
|||
---
|
||||
id: detection-sensor
|
||||
title: Detection Sensor Module Usage
|
||||
slug: /settings/moduleconfig/detection-sensor
|
||||
sidebar_label: Detection Sensor
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
The Detection Sensor module allows you to configure a GPIO pin to be monitored for a specified high/low status and send text alerts over the Detection Sensor portnum when an event is detected. This is particularly useful for motion detection sensors, reed switches, and other open / closed state systems in which notifications over the mesh are desired. Config options are: Enabled, Minimum Broadcast Interval, State Broadcast Interval, Send Bell, Name, Monitor Pin, Detection Triggered High, and Use Pull-up.
|
||||
|
||||
In order to use this module, make sure your devices have firmware version 2.2.2 or higher.
|
||||
|
||||
## Detection Sensor Module Config Values
|
||||
|
||||
### Enabled
|
||||
|
||||
Whether the Module is enabled.
|
||||
|
||||
### Minimum Broadcast Interval
|
||||
|
||||
The interval in seconds of how often we can send a message to the mesh when a state change is detected.
|
||||
|
||||
### State Broadcast Interval
|
||||
|
||||
The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes, When set to 0, only state changes will be broadcasted, Works as a sort of status heartbeat for peace of mind.
|
||||
|
||||
### Send Bell
|
||||
|
||||
Send ASCII bell with alert message. Useful for triggering ext. notification on bell
|
||||
name.
|
||||
|
||||
### Friendly Name
|
||||
|
||||
Used to format the message sent to mesh. Example: A name "Motion" would result in a message "Motion detected". Maximum length of 20 characters.
|
||||
|
||||
### Monitor Pin
|
||||
|
||||
The GPIO pin to monitor for state changes.
|
||||
|
||||
### Detection Triggered High
|
||||
|
||||
Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0).
|
||||
|
||||
### Use Pull-up
|
||||
|
||||
Whether or not use INPUT_PULLUP mode for GPIO pin. Only applicable if the board uses pull-up resistors on the pin.
|
||||
|
||||
## Detection Sensor Module Client Availability
|
||||
|
||||
<Tabs
|
||||
groupId="settings"
|
||||
defaultValue="cli"
|
||||
values={[
|
||||
{label: 'Android', value: 'android'},
|
||||
{label: 'Apple', value: 'apple'},
|
||||
{label: 'CLI', value: 'cli'},
|
||||
{label: 'Web', value: 'web'},
|
||||
]}>
|
||||
<TabItem value="android">
|
||||
|
||||
|
||||
:::info
|
||||
All Detection Sensor Module config options are available for Android in app version 2.2.2 and higher.
|
||||
|
||||
1. Open the Meshtastic App
|
||||
2. Navigate to: **Vertical Ellipsis (3 dots top right) > Radio Configuration > Detection Sensor**
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="apple">
|
||||
|
||||
All Detection Sensor Module config options are available on iOS, iPadOS and macOS app versions 2.2.2 and higher at Settings > Modules > Detection Sensor
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cli">
|
||||
|
||||
:::info
|
||||
|
||||
All Detection Sensor Module config options are available in the python CLI version 2.2.2 and higher.
|
||||
|
||||
:::
|
||||
|
||||
Example commands are below:
|
||||
|
||||
```shell title="Enable/Disable the Detection Sensor Module"
|
||||
meshtastic --set detection_sensor.enabled true
|
||||
meshtastic --set detection_sensor.enabled false
|
||||
```
|
||||
|
||||
```shell title="Set the Minimum Broadcast Interval to 90 seconds"
|
||||
meshtastic --set detection_sensor.minimum_broadcast_secs 90
|
||||
```
|
||||
|
||||
```shell title="Set the State Broadcast Interval to 5 minutes"
|
||||
meshtastic --set detection_sensor.state_broadcast_secs 300
|
||||
```
|
||||
|
||||
```shell title="Enable/Disable Send Bell"
|
||||
meshtastic --set detection_sensor.send_bell true
|
||||
meshtastic --set detection_sensor.send_bell false
|
||||
```
|
||||
|
||||
```shell title="Set the friendly name to 'motion'"
|
||||
meshtastic --set detection_sensor.name "motion"
|
||||
```
|
||||
|
||||
```shell title="Set the Monitor Pin to 7"
|
||||
meshtastic --set detection_sensor.monitor_pin 7
|
||||
```
|
||||
|
||||
```shell title="Enable Notifications when the Monitor Pin goes HIGH"
|
||||
meshtastic --set detection_sensor.monitor_pin true
|
||||
```
|
||||
|
||||
```shell title="Enable Notifications when the Monitor Pin goes LOW"
|
||||
meshtastic --set detection_sensor.monitor_pin false
|
||||
```
|
||||
|
||||
```shell title="Enable the use INPUT_PULLUP mode"
|
||||
meshtastic --set detection_sensor.use_pullup true
|
||||
```
|
||||
|
||||
```shell title="Get the Detection Sensor Module Configuration"
|
||||
meshtastic --get detection_sensor
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="web">
|
||||
|
||||
Not yet implemented.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
|
@ -12,6 +12,7 @@ Modules are included in the firmware and allow users to extend the functionality
|
|||
| :------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [Audio](/docs/settings/moduleconfig/audio) | Enable Support for Codec2 Voice Comms on certain devices. |
|
||||
| [Canned Message](/docs/settings/moduleconfig/canned-message) | Set a number of predefined messages to send out directly from the device with the use of an input device like a rotary encoder. |
|
||||
| [Detection Sensor](/docs/settings/moduleconfig/detection-sensor) | Configure a GPIO pin to be monitored for specified high/low status and send text alerts. |
|
||||
| [External Notification](/docs/settings/moduleconfig/external-notification) | Incoming messages are able to alert you using circuits you attach to the device (LEDs, Buzzers, etc). |
|
||||
| [MQTT](/docs/settings/moduleconfig/mqtt) | Forward packets along to an MQTT server. This allows users on the local mesh to communicate with users on another mesh over the internet. |
|
||||
| [Neighbor Info](/docs/settings/moduleconfig/neighbor-info) | Send info on 0-hop neighbors to the mesh. |
|
||||
|
|
28
docs/software/integrations/atak-plugin.mdx
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: integrations-atak-plugin
|
||||
title: ATAK Plugin
|
||||
sidebar_label: ATAK Plugin
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
## Official Meshtastic ATAK Plugin
|
||||
|
||||
Meshtastic can integrate with ATAK on Android using the [Official ATAK Plugin.](https://github.com/meshtastic/ATAK-Plugin)
|
||||
|
||||
### Introduction
|
||||
|
||||
The ATAK plugin does not permit any Meshtastic configuration. The plugin does three things:
|
||||
|
||||
1. Bind to the IMeshService provided by the Meshtastic Android App in order to send.
|
||||
2. Intercept all outgoing ATAK CoT via the ATAK "PreSendProcessor" Interface and send them to the IMeshService.
|
||||
3. Listen for broadcasts from the Meshtastic Android App regarding ATAK_FORWARDER portnum packets.
|
||||
|
||||
The current iteration works very well for ATAK CoT that fit within the 200ish byte range (after we shrink via libcotshirnk) because they fit into a single DataPacket.
|
||||
|
||||
### Instructions
|
||||
|
||||
1. Use the Meshtastic Android App on all party's devices, and ensure they can talk to their local LoRa radio. Confirm they are able to achieve basic text messaging using the App.
|
||||
|
||||
2. With the Meshtastic Android App running in the background (to ensure the IMeshService is alive), launch ATAK (with the Meshtastic ATAK-Plugin installed or install it once ATAK is running) and you should observe a green Meshtastic icon in the bottom right. If the icon is red, then the plugin was not able to bind to the IMeshService provided by the Meshtastic Android App. If this is the case, check to ensure the Meshtastic Android App is functioning. The plugin will reconnect after a failed bind without restarting ATAK.
|
||||
|
||||
3. ATAK PLI and simple map markers will fit within the "small send" DataPackets. Sending larger CoT such as freestyle map drawings or GeoChats will fragment and take longer to send. Don't try to send a bunch without waiting for the previous one to complete.
|
|
@ -11,4 +11,6 @@ Current Meshtastic integrations:
|
|||
|
||||
- [CalTopo / SARTopo](/docs/software/integrations/integrations-caltopo) - Track Meshtastic nodes in CalTopo and SARTopo.
|
||||
|
||||
- [ATAK Plugin](/docs/software/integrations/integrations-atak-plugin) - Official Meshtastic ATAK Plugin for sending CoT to IMeshService in the Meshtastic Android app.
|
||||
|
||||
Support for the integrated products should be sought from their respective authors or vendors.
|
||||
|
|
|
@ -15,11 +15,6 @@ const config = {
|
|||
organizationName: "meshtastic",
|
||||
projectName: "meshtastic",
|
||||
themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ {
|
||||
announcementBar: {
|
||||
id: "2_0",
|
||||
content:
|
||||
'🎉 Meshtastic 2.0 Has Now Launched! Check it Out <a href="/2.0">Here</a> 🎉',
|
||||
},
|
||||
docs: {
|
||||
sidebar: {
|
||||
autoCollapseCategories: true,
|
||||
|
|
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;
|
||||
|
|