Add Trunk and lint non mdx files

This commit is contained in:
Sacha Weatherstone 2023-01-19 22:41:44 +10:00
parent f924461192
commit d94b0e9a42
No known key found for this signature in database
GPG key ID: 7AB2D7E206124B31
88 changed files with 1919 additions and 1539 deletions

View file

@ -1,4 +1,9 @@
{ {
"plugins": ["@docusaurus"], "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@docusaurus/recommended"],
"extends": ["plugin:@docusaurus/recommended"] "parser": "@typescript-eslint/parser",
} "plugins": ["@docusaurus", "@typescript-eslint"],
"root": true
}

View file

@ -6,9 +6,9 @@ module.exports = {
overrides: { overrides: {
removeViewBox: false, // https://github.com/svg/svgo/issues/1128 removeViewBox: false, // https://github.com/svg/svgo/issues/1128
sortAttrs: true, sortAttrs: true,
removeOffCanvasPaths: true, removeOffCanvasPaths: true
}, }
}, }
}, }
], ]
}; };

View file

@ -12,7 +12,6 @@ lint:
- actionlint@1.6.22 - actionlint@1.6.22
- gitleaks@8.15.2 - gitleaks@8.15.2
- git-diff-check - git-diff-check
- oxipng@8.0.0
- shellcheck@0.9.0 - shellcheck@0.9.0
- prettier@2.8.2 - prettier@2.8.2
- shfmt@3.5.0 - shfmt@3.5.0

View file

@ -1,5 +1,3 @@
{ {
"recommendations": [ "recommendations": ["trunk.io"]
"trunk.io" }
]
}

View file

@ -1,4 +1,7 @@
{ {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "trunk.io" "editor.defaultFormatter": "trunk.io",
} "files.associations": {
"*.mdx": "markdown"
}
}

View file

@ -11,7 +11,7 @@ Website and documentation source for the Meshtastic project.
## Stats ## Stats
![Alt](https://repobeats.axiom.co/api/embed/9ef7282debe009789c697432a86499ac2b058a86.svg 'Repobeats analytics image') ![Alt](https://repobeats.axiom.co/api/embed/9ef7282debe009789c697432a86499ac2b058a86.svg "Repobeats analytics image")
## Development & Building ## Development & Building

View file

@ -4,4 +4,4 @@ collapsible: true # make the category collapsible
link: link:
type: generated-index type: generated-index
title: About Meshtastic title: About Meshtastic
slug: /about slug: /about

View file

@ -4,4 +4,4 @@ position: 4
link: link:
type: generated-index type: generated-index
title: Configuration title: Configuration
slug: /settings slug: /settings

View file

@ -4,4 +4,4 @@ position: 7
link: link:
type: generated-index type: generated-index
title: Developers title: Developers
slug: /developers slug: /developers

View file

@ -4,4 +4,4 @@ position: 1
link: link:
slug: /development/device slug: /development/device
type: generated-index type: generated-index
title: Device title: Device

View file

@ -1,8 +1,7 @@
position: 1 # float position is supported position: 1 # float position is supported
label: 'Style Guides' label: "Style Guides"
collapsible: true # make the category collapsible collapsible: true # make the category collapsible
link: link:
slug: /development/docs/style-guide slug: /development/docs/style-guide
type: generated-index type: generated-index
title: Style Guides title: Style Guides

View file

@ -4,4 +4,4 @@ position: 1
link: link:
type: generated-index type: generated-index
title: Firmware title: Firmware
slug: /development/firmware slug: /development/firmware

View file

@ -4,4 +4,4 @@ position: 20
link: link:
type: generated-index type: generated-index
slug: /development/reference slug: /development/reference
title: Reference Material title: Reference Material

View file

@ -4,4 +4,4 @@ position: 2
link: link:
type: generated-index type: generated-index
title: Flashing Firmware title: Flashing Firmware
slug: /getting-started/flashing-firmware slug: /getting-started/flashing-firmware

View file

@ -4,4 +4,4 @@ position: 1
link: link:
type: generated-index type: generated-index
title: Installing Serial Drivers title: Installing Serial Drivers
slug: /getting-started/serial-drivers slug: /getting-started/serial-drivers

View file

@ -4,4 +4,4 @@ position: 5
link: link:
type: generated-index type: generated-index
title: Supported Hardware title: Supported Hardware
slug: hardware slug: hardware

View file

@ -4,4 +4,4 @@ position: 2
link: link:
type: generated-index type: generated-index
title: Antennas title: Antennas
slug: antenna slug: antenna

View file

@ -4,4 +4,4 @@ position: 1
link: link:
type: generated-index type: generated-index
title: Supported Devices title: Supported Devices
slug: supported-hardware slug: supported-hardware

View file

@ -4,4 +4,4 @@ position: 6
link: link:
type: generated-index type: generated-index
title: Software title: Software
slug: /software slug: /software

View file

@ -3,4 +3,4 @@ collapsible: true
position: 1 position: 1
link: link:
type: generated-index type: generated-index
title: Android App title: Android App

View file

@ -3,4 +3,4 @@ collapsible: true
position: 2 position: 2
link: link:
type: generated-index type: generated-index
title: Apple Applications title: Apple Applications

View file

@ -1,144 +1,145 @@
// @ts-check // @ts-check
require('dotenv').config(); require("dotenv").config();
/** @type {import('@docusaurus/types').Config} */ /** @type {import('@docusaurus/types').Config} */
const config = { const config = {
title: 'Meshtastic', title: "Meshtastic",
tagline: tagline:
'An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices', "An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices",
url: 'https://meshtastic.org', url: "https://meshtastic.org",
baseUrl: '/', baseUrl: "/",
onBrokenLinks: 'throw', onBrokenLinks: "throw",
onBrokenMarkdownLinks: 'warn', onBrokenMarkdownLinks: "warn",
favicon: 'design/web/favicon.ico', favicon: "design/web/favicon.ico",
organizationName: 'meshtastic', organizationName: "meshtastic",
projectName: 'meshtastic', projectName: "meshtastic",
themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ {
announcementBar: { announcementBar: {
id: '2_0', id: "2_0",
content: content:
'🎉 Meshtastic 2.0 Has Now Launched! Check it Out <a href="/2.0">Here</a> 🎉', '🎉 Meshtastic 2.0 Has Now Launched! Check it Out <a href="/2.0">Here</a> 🎉'
}, },
docs: { docs: {
sidebar: { sidebar: {
autoCollapseCategories: true, autoCollapseCategories: true
}, }
}, },
navbar: { navbar: {
title: 'Meshtastic', title: "Meshtastic",
hideOnScroll: true, hideOnScroll: true,
logo: { logo: {
alt: 'Meshtastic Logo', alt: "Meshtastic Logo",
src: 'design/logo/svg/Mesh_Logo_Black.svg', src: "design/logo/svg/Mesh_Logo_Black.svg",
srcDark: 'design/logo/svg/Mesh_Logo_White.svg', srcDark: "design/logo/svg/Mesh_Logo_White.svg"
}, },
items: [ items: [
{ {
label: 'Docs', label: "Docs",
to: 'docs/introduction', to: "docs/introduction"
}, },
{ {
label: 'Downloads', label: "Downloads",
to: 'downloads', to: "downloads"
}, },
{ {
label: 'About', label: "About",
position: 'right', position: "right",
items: [ items: [
{ {
label: 'Introduction', label: "Introduction",
to: 'docs/introduction', to: "docs/introduction"
}, },
{ {
label: 'Getting Started', label: "Getting Started",
to: 'docs/getting-started', to: "docs/getting-started"
}, },
{ {
label: 'Contributing', label: "Contributing",
to: 'docs/contributing', to: "docs/contributing"
}, },
{ {
label: 'Legal', label: "Legal",
to: 'docs/legal', to: "docs/legal"
}, },
{ {
label: 'FAQs', label: "FAQs",
to: 'docs/faq', to: "docs/faq"
}, }
], ]
}, },
{ {
href: 'https://github.com/meshtastic', href: "https://github.com/meshtastic",
position: 'right', position: "right",
className: 'header-github-link', className: "header-github-link",
'aria-label': 'GitHub repository', "aria-label": "GitHub repository"
}, }
], ]
}, },
footer: { footer: {
copyright: `<a href="https://vercel.com/?utm_source=meshtastic&utm_campaign=oss">Powered by ▲ Vercel</a> | Meshtastic® is a registered trademark of Meshtastic LLC. | <a href="/docs/legal">Legal Information</a>.`, copyright: `<a href="https://vercel.com/?utm_source=meshtastic&utm_campaign=oss">Powered by ▲ Vercel</a> | Meshtastic® is a registered trademark of Meshtastic LLC. | <a href="/docs/legal">Legal Information</a>.`
}, },
algolia: { algolia: {
appId: 'IG2GQB8L3V', appId: "IG2GQB8L3V",
apiKey: '2e4348812173ec7ea6f7879c7032bb21', // trunk-ignore(gitleaks/generic-api-key)
indexName: 'meshtastic', apiKey: "2e4348812173ec7ea6f7879c7032bb21",
indexName: "meshtastic",
contextualSearch: false, contextualSearch: false,
searchPagePath: 'search', searchPagePath: "search"
}, },
colorMode: { colorMode: {
respectPrefersColorScheme: true, respectPrefersColorScheme: true
}, },
mermaid: { mermaid: {
theme: {light: 'base', dark: 'base'}, theme: { light: "base", dark: "base" },
options: { options: {
themeVariables: { themeVariables: {
primaryColor: '#67EA94', primaryColor: "#67EA94",
primaryTextColor: 'var(--tw-prose-headings)', primaryTextColor: "var(--tw-prose-headings)",
primaryBorderColor: '#4D4D4D', primaryBorderColor: "#4D4D4D",
lineColor: '#EAD667', lineColor: "#EAD667",
secondaryColor: '#EA67BD', secondaryColor: "#EA67BD",
tertiaryColor: '#677CEA' tertiaryColor: "#677CEA"
}, }
}, }
} }
}, },
plugins: [ plugins: [
() => { () => {
return { return {
name: 'docusaurus-tailwindcss', name: "docusaurus-tailwindcss",
configurePostCss(postcssOptions) { configurePostCss(postcssOptions) {
postcssOptions.plugins.push(require('tailwindcss')); postcssOptions.plugins.push(require("tailwindcss"));
postcssOptions.plugins.push(require('autoprefixer')); postcssOptions.plugins.push(require("autoprefixer"));
return postcssOptions; return postcssOptions;
}, }
}; };
}, }
], ],
presets: [ presets: [
[ [
'@docusaurus/preset-classic', "@docusaurus/preset-classic",
/** @type {import('@docusaurus/preset-classic').Options} */ /** @type {import('@docusaurus/preset-classic').Options} */
{ {
docs: { docs: {
sidebarPath: require.resolve('./sidebars.js'), sidebarPath: require.resolve("./sidebars.js"),
editUrl: 'https://github.com/meshtastic/meshtastic/edit/master/', editUrl: "https://github.com/meshtastic/meshtastic/edit/master/",
breadcrumbs: false, breadcrumbs: false,
showLastUpdateAuthor: true, showLastUpdateAuthor: true
}, },
theme: { theme: {
customCss: require.resolve('./src/css/custom.css'), customCss: require.resolve("./src/css/custom.css")
}, }
}, }
], ]
], ],
customFields: { customFields: {
API_URL: process.env.API_URL, API_URL: process.env.API_URL
}, },
markdown: { markdown: {
mermaid: true, mermaid: true
}, },
themes: ['@docusaurus/theme-mermaid'], themes: ["@docusaurus/theme-mermaid"]
}; };
module.exports = config; module.exports = config;

View file

@ -8,9 +8,7 @@
"start": "docusaurus start", "start": "docusaurus start",
"build": "docusaurus build", "build": "docusaurus build",
"serve": "docusaurus serve", "serve": "docusaurus serve",
"clear": "docusaurus clear", "clear": "docusaurus clear"
"format": "prettier --write 'docs/**/*{.md,.mdx}' 'src/**/*.{ts,tsx}'",
"lint": "eslint src/**/*.{ts,tsx}"
}, },
"dependencies": { "dependencies": {
"@algolia/client-search": "^4.14.3", "@algolia/client-search": "^4.14.3",
@ -22,9 +20,12 @@
"@heroicons/react": "^2.0.13", "@heroicons/react": "^2.0.13",
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"@meshtastic/meshtasticjs": "2.0.13-0", "@meshtastic/meshtasticjs": "2.0.13-0",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"eslint": "^8.32.0",
"framer-motion": "^6.5.1", "framer-motion": "^6.5.1",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"react": "^17.0.2", "react": "^17.0.2",
@ -37,6 +38,7 @@
"use-breakpoint": "^3.0.6" "use-breakpoint": "^3.0.6"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/eslint-plugin": "^2.2.0",
"@docusaurus/module-type-aliases": "2.2.0", "@docusaurus/module-type-aliases": "2.2.0",
"@tailwindcss/typography": "^0.5.9", "@tailwindcss/typography": "^0.5.9",
"@tsconfig/docusaurus": "^1.0.6", "@tsconfig/docusaurus": "^1.0.6",

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
import React from 'react'; import React from "react";
export const BatteryCalculator = (): JSX.Element => { export const BatteryCalculator = (): JSX.Element => {
return ( return (
@ -6,7 +6,7 @@ export const BatteryCalculator = (): JSX.Element => {
<div className="card__header"> <div className="card__header">
<h3>Battery Calculator</h3> <h3>Battery Calculator</h3>
</div> </div>
<div className="card__body" style={{ display: 'flex', gap: '2rem' }}> <div className="card__body" style={{ display: "flex", gap: "2rem" }}>
<div> <div>
<input placeholder="Search" /> <input placeholder="Search" />
<input placeholder="Search" /> <input placeholder="Search" />

View file

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

View file

@ -1,4 +1,4 @@
import React from 'react'; import React from "react";
export interface ColorModeProps { export interface ColorModeProps {
children: React.ReactNode; children: React.ReactNode;

View file

@ -1,8 +1,8 @@
import React from 'react'; import React from "react";
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from "framer-motion";
import { Dialog } from '@headlessui/react'; import { Dialog } from "@headlessui/react";
export interface ModalProps { export interface ModalProps {
open: boolean; open: boolean;

View file

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

View file

@ -1,4 +1,4 @@
import React from 'react'; import React from "react";
export interface BadgeProps { export interface BadgeProps {
name: string; name: string;

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import { Tab } from '@headlessui/react'; import { Tab } from "@headlessui/react";
export interface CardTabProps { export interface CardTabProps {
title: string; title: string;
@ -11,7 +11,7 @@ export const CardTab = ({ title }: CardTabProps): JSX.Element => {
<Tab <Tab
className={({ selected }) => className={({ selected }) =>
`w-1/3 truncate rounded-md px-3 py-2 text-sm font-medium hover:bg-tertiary ${ `w-1/3 truncate rounded-md px-3 py-2 text-sm font-medium hover:bg-tertiary ${
selected ? 'bg-secondary shadow-md' : '' selected ? "bg-secondary shadow-md" : ""
}` }`
} }
> >

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import { IDevice, Stability } from '@site/src/data/device'; import { IDevice, Stability } from "@site/src/data/device";
export interface HardwareCardProps { export interface HardwareCardProps {
device: IDevice; device: IDevice;
@ -9,7 +9,7 @@ export interface HardwareCardProps {
export const HardwareCard = ({ export const HardwareCard = ({
device, device,
setDevice, setDevice
}: HardwareCardProps): JSX.Element => { }: HardwareCardProps): JSX.Element => {
return ( return (
<li <li
@ -41,12 +41,12 @@ export const HardwareCard = ({
<div <div
className={`my-auto h-3 w-3 rounded-full ${ className={`my-auto h-3 w-3 rounded-full ${
device.misc.Stability === Stability.Broken device.misc.Stability === Stability.Broken
? 'bg-red-500' ? "bg-red-500"
: device.misc.Stability === Stability.Unstable : device.misc.Stability === Stability.Unstable
? 'bg-orange-500' ? "bg-orange-500"
: device.misc.Stability === Stability.Semi : device.misc.Stability === Stability.Semi
? 'bg-cyan-500' ? "bg-cyan-500"
: 'bg-green-500' : "bg-green-500"
}`} }`}
/> />
<div className="my-auto">{Stability[device.misc.Stability]}</div> <div className="my-auto">{Stability[device.misc.Stability]}</div>

View file

@ -1,21 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from "framer-motion";
import { FiBluetooth, FiChevronRight, FiWifi, FiX } from 'react-icons/fi'; import { FiBluetooth, FiChevronRight, FiWifi, FiX } from "react-icons/fi";
import { useBreakpoint } from 'use-breakpoint'; import { useBreakpoint } from "use-breakpoint";
import { Tab } from '@headlessui/react'; import { Tab } from "@headlessui/react";
import type { IDevice } from '@site/src/data/device'; import type { IDevice } from "@site/src/data/device";
import { Button } from '../../components/Button'; import { Button } from "../../components/Button";
import { BREAKPOINTS } from '../../utils/breakpoints'; import { BREAKPOINTS } from "../../utils/breakpoints";
import { Modal } from '../Modal'; import { Modal } from "../Modal";
import { Badge } from './Badge'; import { Badge } from "./Badge";
import { CardTab } from './CardTab'; import { CardTab } from "./CardTab";
import { InfoTab } from './Tabs/InfoTab'; import { InfoTab } from "./Tabs/InfoTab";
import { PinoutTab } from './Tabs/PinoutTab'; import { PinoutTab } from "./Tabs/PinoutTab";
import { PowerTab } from './Tabs/PowerTab'; import { PowerTab } from "./Tabs/PowerTab";
import { VariantSelectButton } from './VariantSelectButton'; import { VariantSelectButton } from "./VariantSelectButton";
export interface HardwareModal { export interface HardwareModal {
device: IDevice; device: IDevice;
@ -26,10 +26,10 @@ export interface HardwareModal {
export const HardwareModal = ({ export const HardwareModal = ({
device, device,
open, open,
close, close
}: HardwareModal): JSX.Element => { }: HardwareModal): JSX.Element => {
const [hideDetails, setHideDetails] = useState(false); const [hideDetails, setHideDetails] = useState(false);
const { breakpoint } = useBreakpoint(BREAKPOINTS, 'md'); const { breakpoint } = useBreakpoint(BREAKPOINTS, "md");
return ( return (
<Modal open={open} onClose={close}> <Modal open={open} onClose={close}>
@ -42,22 +42,22 @@ export const HardwareModal = ({
<motion.div <motion.div
layout layout
animate={ animate={
breakpoint === 'sm' breakpoint === "sm"
? hideDetails ? hideDetails
? 'hiddenSm' ? "hiddenSm"
: 'visibleSm' : "visibleSm"
: hideDetails : hideDetails
? 'hidden' ? "hidden"
: 'visible' : "visible"
} }
variants={{ variants={{
hidden: { width: '100%', height: '100%' }, hidden: { width: "100%", height: "100%" },
hiddenSm: { height: '100%', width: '100%' }, hiddenSm: { height: "100%", width: "100%" },
visible: { width: '20%', height: '100%' }, visible: { width: "20%", height: "100%" },
visibleSm: { height: '33%', width: '100%' }, visibleSm: { height: "33%", width: "100%" }
}} }}
transition={{ transition={{
type: 'just', type: "just"
}} }}
className="flex flex-col md:h-full md:flex-row" className="flex flex-col md:h-full md:flex-row"
> >
@ -74,19 +74,19 @@ export const HardwareModal = ({
<div className="absolute -bottom-5 z-20 flex w-full md:bottom-auto md:-right-5 md:h-full md:w-auto"> <div className="absolute -bottom-5 z-20 flex w-full md:bottom-auto md:-right-5 md:h-full md:w-auto">
<Button <Button
animate={ animate={
breakpoint === 'sm' breakpoint === "sm"
? hideDetails ? hideDetails
? 'hiddenSm' ? "hiddenSm"
: 'visibleSm' : "visibleSm"
: hideDetails : hideDetails
? 'hidden' ? "hidden"
: 'visible' : "visible"
} }
variants={{ variants={{
hidden: { rotate: 180 }, hidden: { rotate: 180 },
hiddenSm: { rotate: -90 }, hiddenSm: { rotate: -90 },
visible: { rotate: 0 }, visible: { rotate: 0 },
visibleSm: { rotate: 90 }, visibleSm: { rotate: 90 }
}} }}
onClick={() => { onClick={() => {
setHideDetails(!hideDetails); setHideDetails(!hideDetails);
@ -103,7 +103,7 @@ export const HardwareModal = ({
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className={`absolute -bottom-5 z-20 flex md:mt-0 md:hidden md:pb-2 ${ className={`absolute -bottom-5 z-20 flex md:mt-0 md:hidden md:pb-2 ${
hideDetails ? 'opacity-0' : 'opacity-100' hideDetails ? "opacity-0" : "opacity-100"
}`} }`}
> >
<VariantSelectButton options={device.variants} /> <VariantSelectButton options={device.variants} />
@ -137,7 +137,7 @@ export const HardwareModal = ({
</motion.div> </motion.div>
<div <div
className={`h-7 bg-base opacity-0 md:h-auto md:w-7 ${ className={`h-7 bg-base opacity-0 md:h-auto md:w-7 ${
hideDetails ? 'flex' : 'hidden' hideDetails ? "flex" : "hidden"
}`} }`}
/> />
</motion.div> </motion.div>
@ -148,7 +148,7 @@ export const HardwareModal = ({
</div> </div>
<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 ${ 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' hideDetails ? "opacity-0" : "opacity-100"
}`} }`}
> >
<Tab.Group <Tab.Group

View file

@ -1,8 +1,8 @@
import React from 'react'; import React from "react";
import { Tab } from '@headlessui/react'; import { Tab } from "@headlessui/react";
import type { IDevice } from '../../../data/device'; import type { IDevice } from "../../../data/device";
export interface InfoTabProps { export interface InfoTabProps {
device: IDevice; device: IDevice;

View file

@ -1,8 +1,8 @@
import React from 'react'; import React from "react";
import { Tab } from '@headlessui/react'; import { Tab } from "@headlessui/react";
import type { IDevice } from '../../../data/device'; import type { IDevice } from "../../../data/device";
export interface PinoutTabProps { export interface PinoutTabProps {
device: IDevice; device: IDevice;
@ -14,13 +14,13 @@ export const PinoutTab = ({ device }: PinoutTabProps): JSX.Element => {
<div className="m-auto flex gap-4 rounded-lg bg-slate-700 px-2 py-1 shadow-md"> <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(0, device.misc.pinoutSplit),
device.pinout.slice(device.misc.pinoutSplit, device.pinout.length), device.pinout.slice(device.misc.pinoutSplit, device.pinout.length)
].map((group, index) => ( ].map((group, index) => (
<div key={index}> <div key={index}>
{group.map((pin, pinIndex) => ( {group.map((pin, pinIndex) => (
<div <div
className={`flex gap-1 ${ className={`flex gap-1 ${
index === 0 ? 'flex-row' : 'flex-row-reverse' index === 0 ? "flex-row" : "flex-row-reverse"
}`} }`}
key={pinIndex} key={pinIndex}
> >

View file

@ -1,13 +1,13 @@
import React from 'react'; import React from "react";
import { Tab } from '@headlessui/react'; import { Tab } from "@headlessui/react";
import type { IDevice } from '../../../data/device'; import type { IDevice } from "../../../data/device";
export interface PowerTabProps { export interface PowerTabProps {
device: IDevice; device: IDevice;
} }
export const PowerTab = ({ device }: PowerTabProps): JSX.Element => { export const PowerTab = ({}: PowerTabProps): JSX.Element => {
return <Tab.Panel className="h-32">Content 1</Tab.Panel>; return <Tab.Panel className="h-32">Content 1</Tab.Panel>;
}; };

View file

@ -1,18 +1,18 @@
import React, { Fragment, useState } from 'react'; import React, { Fragment, useState } from "react";
import { motion } from 'framer-motion'; import { motion } from "framer-motion";
import { FiCheck } from 'react-icons/fi'; import { FiCheck } from "react-icons/fi";
import { HiSelector } from 'react-icons/hi'; import { HiSelector } from "react-icons/hi";
import { Listbox, Transition } from '@headlessui/react'; import { Listbox, Transition } from "@headlessui/react";
import type { Variant } from '@site/src/data/device.js'; import type { Variant } from "@site/src/data/device.js";
export interface VariantSelectButtonProps { export interface VariantSelectButtonProps {
options: Variant[]; options: Variant[];
} }
export const VariantSelectButton = ({ export const VariantSelectButton = ({
options, options
}: VariantSelectButtonProps): JSX.Element => { }: VariantSelectButtonProps): JSX.Element => {
const [selected, setSelected] = useState(options[options.length - 1]); const [selected, setSelected] = useState(options[options.length - 1]);
@ -23,7 +23,7 @@ export const VariantSelectButton = ({
<div className="relative select-none"> <div className="relative select-none">
<Listbox.Button as={Fragment}> <Listbox.Button as={Fragment}>
<motion.button <motion.button
whileHover={{ backgroundColor: 'var(--tertiary)' }} whileHover={{ backgroundColor: "var(--tertiary)" }}
whileTap={{ scale: 0.99 }} 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" 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"
> >
@ -50,7 +50,7 @@ export const VariantSelectButton = ({
key={index} key={index}
className={({ active }) => className={({ active }) =>
`relative cursor-default select-none py-2 pl-3 pr-9 ${ `relative cursor-default select-none py-2 pl-3 pr-9 ${
active ? 'bg-secondary' : '' active ? "bg-secondary" : ""
}` }`
} }
value={variant} value={variant}
@ -59,7 +59,7 @@ export const VariantSelectButton = ({
<> <>
<span <span
className={`block truncate ${ className={`block truncate ${
selected ? 'font-semibold' : 'font-normal' selected ? "font-semibold" : "font-normal"
}`} }`}
> >
{variant.name} {variant.name}
@ -68,7 +68,7 @@ export const VariantSelectButton = ({
{selected ? ( {selected ? (
<span <span
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${ className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
active ? '' : 'text-primaryInv' active ? "" : "text-primaryInv"
}`} }`}
> >
<FiCheck className="h-5 w-5" aria-hidden="true" /> <FiCheck className="h-5 w-5" aria-hidden="true" />

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import { FiExternalLink } from 'react-icons/fi'; import { FiExternalLink } from "react-icons/fi";
export interface SocialCardProps { export interface SocialCardProps {
children: React.ReactNode; children: React.ReactNode;
@ -11,7 +11,7 @@ export interface SocialCardProps {
export const SocialCard = ({ export const SocialCard = ({
children, children,
color, color,
link, link
}: SocialCardProps): JSX.Element => { }: SocialCardProps): JSX.Element => {
return ( return (
<div <div

View file

@ -22,14 +22,14 @@
--mute: #6b7280; --mute: #6b7280;
--primaryInv: #242526; --primaryInv: #242526;
--secondaryInv: #18191a; --secondaryInv: #18191a;
--tertiaryInv: #4C4E50; --tertiaryInv: #4c4e50;
} }
[data-theme="dark"] { [data-theme="dark"] {
--base: #38393B; --base: #38393b;
--primary: #242526; --primary: #242526;
--secondary: #18191a; --secondary: #18191a;
--tertiary: #4C4E50; --tertiary: #4c4e50;
--mute: #9ca3af; --mute: #9ca3af;
--primaryInv: #ffffff; --primaryInv: #ffffff;
--secondaryInv: #e5e7eb; --secondaryInv: #e5e7eb;
@ -143,53 +143,59 @@ a + .navbar__link > svg {
@apply prose-invert; @apply prose-invert;
} }
[data-theme="dark"] .hideDark { [data-theme="dark"] .hideDark {
@apply hidden @apply hidden;
} }
[data-theme="dark"] .hideLight { [data-theme="dark"] .hideLight {
@apply block @apply block;
} }
.hideLight { .hideLight {
@apply hidden @apply hidden;
} }
.hideDark { .hideDark {
@apply block @apply block;
} }
} }
.markdown :where(li):not(:where([class~=not-prose] *)){ .markdown :where(li):not(:where([class~="not-prose"] *)) {
margin-bottom:0; margin-bottom: 0;
margin-top:0;
}
.markdown :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)){
margin-bottom:0;
margin-top: 0; margin-top: 0;
} }
h1, h2, h3, h4, h5, h6 { .markdown
font-weight: var(--ifm-heading-font-weight) :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"] *)) {
} margin-bottom: 0;
margin-top: 0;
h1 { }
font-size: var(--ifm-h1-font-size)
} h1,
h2,
h2 { h3,
font-size: var(--ifm-h2-font-size) h4,
} h5,
h6 {
h3 { font-weight: var(--ifm-heading-font-weight);
font-size: var(--ifm-h3-font-size) }
}
h1 {
h4 { font-size: var(--ifm-h1-font-size);
font-size: var(--ifm-h4-font-size) }
}
h2 {
h5 { font-size: var(--ifm-h2-font-size);
font-size: var(--ifm-h5-font-size) }
}
h3 {
h6 { font-size: var(--ifm-h3-font-size);
font-size: var(--ifm-h6-font-size) }
}
h4 {
font-size: var(--ifm-h4-font-size);
}
h5 {
font-size: var(--ifm-h5-font-size);
}
h6 {
font-size: var(--ifm-h6-font-size);
}

View file

@ -7,12 +7,12 @@ export type DeepPartial<T> = T extends object
export enum UseCase { export enum UseCase {
Solar, Solar,
Router, Router,
Portable, Portable
} }
enum PinUsage { enum PinUsage {
LoRa, LoRa,
GNSS, GNSS
} }
export interface Pin { export interface Pin {
@ -25,38 +25,38 @@ export interface Pin {
usage?: PinUsage; usage?: PinUsage;
} }
export type DeviceName = 'tbeam' | 'techo'; export type DeviceName = "tbeam" | "techo";
export type BLEVersion = '4.2' | '5.0'; export type BLEVersion = "4.2" | "5.0";
export type AntennaType = 'Integrated'; export type AntennaType = "Integrated";
export type Chipset = 'ESP32' | 'NRF52'; export type Chipset = "ESP32" | "NRF52";
export type Frequency = 433 | 868 | 915 | 923; export type Frequency = 433 | 868 | 915 | 923;
export type SerialAdapter = 'CP210X' | 'CH9102'; export type SerialAdapter = "CP210X" | "CH9102";
export type GNSSModule = 'NEO-6M' | 'NEO-8M'; export type GNSSModule = "NEO-6M" | "NEO-8M";
export type LORAModule = 'SX1276' | 'SX1262'; export type LORAModule = "SX1276" | "SX1262";
export type Variant = DeepPartial<Omit<IDevice, 'variants'>> & { name: string }; export type Variant = DeepPartial<Omit<IDevice, "variants">> & { name: string };
export enum Stability { export enum Stability {
Stable, Stable,
Semi, Semi,
Unstable, Unstable,
Broken, Broken
} }
export type Module = export type Module =
| 'cannedMessage' | "cannedMessage"
| 'externalNotification' | "externalNotification"
| 'rangeTest' | "rangeTest"
| 'rotaryEncoder' | "rotaryEncoder"
| 'storeAndForward' | "storeAndForward"
| 'telemetry'; | "telemetry";
export interface IDevice { export interface IDevice {
name: string; name: string;

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const heltec: IDevice = { export const heltec: IDevice = {
name: 'Heltec', name: "Heltec",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400', Gradient: "bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400"
}, },
images: { images: {
Front: '/img/hardware/heltec-v2.png', Front: "/img/hardware/heltec-v2.png",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const hydra: IDevice = { export const hydra: IDevice = {
name: 'Hydra', name: "Hydra",
misc: { misc: {
Stability: Stability.Stable, Stability: Stability.Stable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-indigo-200 via-red-200 to-yellow-100', Gradient: "bg-gradient-to-r from-indigo-200 via-red-200 to-yellow-100"
}, },
images: { images: {
Front: '/img/hardware/Hydra-PCB.2.1.svg', Front: "/img/hardware/Hydra-PCB.2.1.svg",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const nano_g1: IDevice = { export const nano_g1: IDevice = {
name: 'Nano G1', name: "Nano G1",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-green-200 to-green-500', Gradient: "bg-gradient-to-r from-green-200 to-green-500"
}, },
images: { images: {
Front: '/img/hardware/nano_g1_front.svg', Front: "/img/hardware/nano_g1_front.svg",
Back: '/img/hardware/nano_g1_back.svg', Back: "/img/hardware/nano_g1_back.svg"
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const rak19001: IDevice = { export const rak19001: IDevice = {
name: 'WisBlock 19001', name: "WisBlock 19001",
misc: { misc: {
Stability: Stability.Stable, Stability: Stability.Stable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-indigo-300 to-purple-400', Gradient: "bg-gradient-to-r from-indigo-300 to-purple-400"
}, },
images: { images: {
Front: '/img/hardware/rak/RAK19001.png', Front: "/img/hardware/rak/RAK19001.png",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const rak19003: IDevice = { export const rak19003: IDevice = {
name: 'WisBlock 19003', name: "WisBlock 19003",
misc: { misc: {
Stability: Stability.Stable, Stability: Stability.Stable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-b from-orange-500 to-yellow-300', Gradient: "bg-gradient-to-b from-orange-500 to-yellow-300"
}, },
images: { images: {
Front: '/img/hardware/rak/RAK19003.png', Front: "/img/hardware/rak/RAK19003.png",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,277 +1,277 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const tbeam: IDevice = { export const tbeam: IDevice = {
name: 'T-Beam', name: "T-Beam",
misc: { misc: {
Stability: Stability.Stable, Stability: Stability.Stable,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500', Gradient: "bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500",
pinoutSplit: 13, pinoutSplit: 13
}, },
images: { images: {
Front: '/img/hardware/tbeam-v1.1.svg', Front: "/img/hardware/tbeam-v1.1.svg",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ],
pinout: [ pinout: [
{ {
label: 'VP', label: "VP",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'VN', label: "VN",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'RST', label: "RST",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '15', label: "15",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '35', label: "35",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '32', label: "32",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '33', label: "33",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '25', label: "25",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '14', label: "14",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '13', label: "13",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '2', label: "2",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'GND', label: "GND",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '5V', label: "5V",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'TX', label: "TX",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'RX', label: "RX",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '23', label: "23",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '4', label: "4",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '0', label: "0",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'GND', label: "GND",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '3V3', label: "3V3",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'GND', label: "GND",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '22', label: "22",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '21', label: "21",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: '3.3V', label: "3.3V",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'LoRa2', label: "LoRa2",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, },
{ {
label: 'LoRa1', label: "LoRa1",
name: 'IO1', name: "IO1",
offset: { offset: {
x: 5, x: 5,
y: 5, y: 5
}, }
}, }
], ]
}; };

View file

@ -1,66 +1,66 @@
import { IDevice, Stability, UseCase } from '../device'; import { IDevice, Stability, UseCase } from "../device";
export const techo: IDevice = { export const techo: IDevice = {
name: 'T-Echo', name: "T-Echo",
misc: { misc: {
Stability: Stability.Semi, Stability: Stability.Semi,
SuggestedUse: [UseCase.Portable], SuggestedUse: [UseCase.Portable],
Gradient: 'bg-gradient-to-r from-gray-700 via-gray-900 to-black', Gradient: "bg-gradient-to-r from-gray-700 via-gray-900 to-black"
}, },
images: { images: {
Front: '/img/hardware/t-echo-lilygo.jpg', Front: "/img/hardware/t-echo-lilygo.jpg",
Back: '', Back: ""
}, },
features: { features: {
BLE: true, BLE: true,
WiFi: true, WiFi: true,
Modules: [ Modules: [
'cannedMessage', "cannedMessage",
'externalNotification', "externalNotification",
'rangeTest', "rangeTest",
'rotaryEncoder', "rotaryEncoder",
'storeAndForward', "storeAndForward",
'telemetry', "telemetry"
], ]
}, },
specifications: { specifications: {
BLEVersion: '4.2', BLEVersion: "4.2",
BLEAntenna: 'Integrated', BLEAntenna: "Integrated",
WiFiVersion: '2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS', WiFiVersion: "2.4GHz 802.11 b/g/n WPA/WPA2/WPA2-Enterprise/SPS",
WiFiAntenna: 'Integrated', WiFiAntenna: "Integrated",
Chipset: 'ESP32', Chipset: "ESP32",
Driver: 'CH9102', Driver: "CH9102",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
FlashSize: 4, FlashSize: 4,
Frequencies: [433, 868, 915, 923], Frequencies: [433, 868, 915, 923],
LoRa: 'SX1262', LoRa: "SX1262",
PSRAM: 8, PSRAM: 8,
RAM: undefined, RAM: undefined
}, },
variants: [ variants: [
{ {
name: 'TBeam 0.7', name: "TBeam 0.7",
misc: { misc: {
Stability: Stability.Unstable, Stability: Stability.Unstable
}, },
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M",
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.0', name: "TBeam 1.0",
specifications: { specifications: {
Frequencies: [868, 915], Frequencies: [868, 915]
}, }
}, },
{ {
name: 'TBeam 1.1', name: "TBeam 1.1",
specifications: { specifications: {
Driver: 'CP210X', Driver: "CP210X",
GNSS: 'NEO-6M', GNSS: "NEO-6M"
}, }
}, }
], ]
}; };

View file

@ -1,11 +1,11 @@
export interface IRegion { export interface IRegion {
name: string; name: string;
freqStart: number; freqStart: number;
freqEnd: number; freqEnd: number;
dutyCycle: number; dutyCycle: number;
spacing: number; spacing: number;
powerLimit: number; powerLimit: number;
audioPermitted: boolean; audioPermitted: boolean;
frequencySwitching: boolean; frequencySwitching: boolean;
wideLora: boolean; wideLora: boolean;
} }

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const ANZ: IRegion = { export const ANZ: IRegion = {
name: "ANZ", name: "ANZ",
freqStart: 915.0, freqStart: 915.0,
freqEnd: 928.0, freqEnd: 928.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 30, powerLimit: 30,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const CN: IRegion = { export const CN: IRegion = {
name: "CN", name: "CN",
freqStart: 470.0, freqStart: 470.0,
freqEnd: 510.0, freqEnd: 510.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 19, powerLimit: 19,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const EU_433: IRegion = { export const EU_433: IRegion = {
name: "EU_433", name: "EU_433",
freqStart: 433.0, freqStart: 433.0,
freqEnd: 434.0, freqEnd: 434.0,
dutyCycle: 10, dutyCycle: 10,
spacing: 0, spacing: 0,
powerLimit: 12, powerLimit: 12,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const EU_868: IRegion = { export const EU_868: IRegion = {
name: "EU_868", name: "EU_868",
freqStart: 869.4, freqStart: 869.4,
freqEnd: 869.65, freqEnd: 869.65,
dutyCycle: 10, dutyCycle: 10,
spacing: 0, spacing: 0,
powerLimit: 27, powerLimit: 27,
audioPermitted: false, audioPermitted: false,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const IN: IRegion = { export const IN: IRegion = {
name: "IN", name: "IN",
freqStart: 865.0, freqStart: 865.0,
freqEnd: 867.0, freqEnd: 867.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 30, powerLimit: 30,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const JP: IRegion = { export const JP: IRegion = {
name: "JP", name: "JP",
freqStart: 920.8, freqStart: 920.8,
freqEnd: 927.8, freqEnd: 927.8,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 16, powerLimit: 16,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const KR: IRegion = { export const KR: IRegion = {
name: "KR", name: "KR",
freqStart: 920.0, freqStart: 920.0,
freqEnd: 925.0, freqEnd: 925.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 0, powerLimit: 0,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const LORA_24: IRegion = { export const LORA_24: IRegion = {
name: "LORA_24", name: "LORA_24",
freqStart: 2400.0, freqStart: 2400.0,
freqEnd: 2483.5, freqEnd: 2483.5,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 10, powerLimit: 10,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: true, wideLora: true
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const NZ_865: IRegion = { export const NZ_865: IRegion = {
name: "NZ_865", name: "NZ_865",
freqStart: 864.0, freqStart: 864.0,
freqEnd: 868.0, freqEnd: 868.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 36, powerLimit: 36,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const RU: IRegion = { export const RU: IRegion = {
name: "RU", name: "RU",
freqStart: 868.7, freqStart: 868.7,
freqEnd: 869.2, freqEnd: 869.2,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 20, powerLimit: 20,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const TH: IRegion = { export const TH: IRegion = {
name: "TH", name: "TH",
freqStart: 920.0, freqStart: 920.0,
freqEnd: 925.0, freqEnd: 925.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 16, powerLimit: 16,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const TW: IRegion = { export const TW: IRegion = {
name: "TW", name: "TW",
freqStart: 920.0, freqStart: 920.0,
freqEnd: 925.0, freqEnd: 925.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 0, powerLimit: 0,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const UNSET: IRegion = { export const UNSET: IRegion = {
name: "UNSET", name: "UNSET",
freqStart: 902.0, freqStart: 902.0,
freqEnd: 928.0, freqEnd: 928.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 30, powerLimit: 30,
audioPermitted: true, audioPermitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

@ -1,13 +1,13 @@
import { IRegion } from '../region'; import { IRegion } from "../region";
export const US: IRegion = { export const US: IRegion = {
name: "US", name: "US",
freqStart: 902.0, freqStart: 902.0,
freqEnd: 928.0, freqEnd: 928.0,
dutyCycle: 100, dutyCycle: 100,
spacing: 0, spacing: 0,
powerLimit: 30, powerLimit: 30,
audioPrmitted: true, audioPrmitted: true,
frequencySwitching: false, frequencySwitching: false,
wideLora: false, wideLora: false
} };

View file

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

View file

@ -1,8 +1,8 @@
import React from 'react'; import React from "react";
import { useLocation } from '@docusaurus/router'; import { useLocation } from "@docusaurus/router";
import { readSearchTags } from '../pages/showcase/_components/TagSelect'; import { readSearchTags } from "../pages/showcase/_components/TagSelect";
export const useSelectedTags = () => { export const useSelectedTags = () => {
const location = useLocation(); const location = useLocation();

View file

@ -1,45 +1,44 @@
import React from 'react'; import React from "react";
import { FiTwitter } from 'react-icons/fi'; import { FiTwitter } from "react-icons/fi";
import { ChevronRightIcon } from '@heroicons/react/20/solid';
import FlipClockCountdown from '@leenguyen/react-flip-clock-countdown';
import Layout from '@theme/Layout';
import { Dark, Light } from '/src/components/ColorMode';
import { ChevronRightIcon } from "@heroicons/react/20/solid";
import FlipClockCountdown from "@leenguyen/react-flip-clock-countdown";
import Layout from "@theme/Layout";
import { Dark, Light } from "/src/components/ColorMode";
const TwoPointZero = (): JSX.Element => { const TwoPointZero = (): JSX.Element => {
const stats = [ const stats = [
{ label: 'Active Nodes', value: 'A Lot!' }, { label: "Active Nodes", value: "A Lot!" },
{ label: 'Community Members', value: '4000+' }, { label: "Community Members", value: "4000+" },
{ label: 'Firmware Commits', value: '4900+' }, { label: "Firmware Commits", value: "4900+" },
{ label: 'Community Donations', value: '$5700+' }, { label: "Community Donations", value: "$5700+" }
]; ];
const logos = [ const logos = [
{ {
name: 'Vercel', name: "Vercel",
url: '/2.0/vercel-logotype-dark.svg', url: "/2.0/vercel-logotype-dark.svg"
}, },
{ {
name: 'Cloudflare', name: "Cloudflare",
url: '/2.0/CF_logo_horizontal_blktype.svg', url: "/2.0/CF_logo_horizontal_blktype.svg"
}, },
{ {
name: 'RAK Wireless', name: "RAK Wireless",
url: '/2.0/RAK-blue-main.svg', url: "/2.0/RAK-blue-main.svg"
}, },
{ {
name: 'Open Collective', name: "Open Collective",
url: '/2.0/opencollectivelogo.svg', url: "/2.0/opencollectivelogo.svg"
}, },
{ {
name: 'LILYGO', name: "LILYGO",
url: '/2.0/LILYGO.png', url: "/2.0/LILYGO.png"
}, },
{ {
name: 'Discord', name: "Discord",
url: '/2.0/discord.svg', url: "/2.0/discord.svg"
}, }
]; ];
return ( return (
<Layout title="Meshtastic 2.0" description="Meshtastic 2.0 Landing Page"> <Layout title="Meshtastic 2.0" description="Meshtastic 2.0 Landing Page">
@ -60,11 +59,11 @@ const TwoPointZero = (): JSX.Element => {
</Dark> </Dark>
<Light> <Light>
<img <img
className="h-11 w-auto" className="h-11 w-auto"
it it
src="/design/logo/svg/Mesh_Logo_Black.svg" src="/design/logo/svg/Mesh_Logo_Black.svg"
alt="Meshtastic Logo" alt="Meshtastic Logo"
/> />
</Light> </Light>
</div> </div>
<div className="mt-20"> <div className="mt-20">
@ -246,8 +245,8 @@ const TwoPointZero = (): JSX.Element => {
<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" /> <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> </svg>
<p className="relative"> <p className="relative">
Meshtastic is the neatest open source project I've ever Meshtastic is the neatest open source project I've
seen! ever seen!
</p> </p>
</div> </div>

View file

@ -4,6 +4,4 @@ title: Meshtastic URL
sidebar_label: c sidebar_label: c
--- ---
# Error : Something happened
Please go to [Android Usage](/docs/software/android/usage#join-a-channel) for more information. Please go to [Android Usage](/docs/software/android/usage#join-a-channel) for more information.

View file

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

View file

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

View file

@ -4,6 +4,4 @@ title: Meshtastic URL
sidebar_label: d sidebar_label: d
--- ---
# Error : Something happened Please go to [Android Usage](/docs/software/android/usage#join-a-channel) for more information.
Please go to [Android Usage](/docs/software/android/usage#join-a-channel) for more information.

View file

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

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import { DeviceFirmwareResource } from '../../../utils/apiTypes.js'; import { DeviceFirmwareResource } from "../../../utils/apiTypes.js";
export interface releaseCardProps { export interface releaseCardProps {
variant: string; variant: string;
@ -11,13 +11,13 @@ export interface releaseCardProps {
export const FirmwareCard = ({ export const FirmwareCard = ({
variant, variant,
description, description,
release, release
}: releaseCardProps): JSX.Element => { }: releaseCardProps): JSX.Element => {
return ( return (
<div className="card m-4 border-2 border-secondary"> <div className="card m-4 border-2 border-secondary">
<div <div
className="card__header" className="card__header"
style={{ display: 'flex', justifyContent: 'space-between' }} style={{ display: "flex", justifyContent: "space-between" }}
> >
<h3>{variant}</h3> <h3>{variant}</h3>
{release?.length && ( {release?.length && (
@ -64,86 +64,86 @@ export const PlaceholderFirmwareCard = (): JSX.Element => {
<div <div
className="card" className="card"
style={{ style={{
width: '100%', width: "100%",
animation: 'pulse 2s infinite', animation: "pulse 2s infinite",
transform: 'scale(1)', transform: "scale(1)",
display: 'flex', display: "flex",
gap: '1rem', gap: "1rem",
padding: '1rem', padding: "1rem"
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
justifyContent: 'space-between', justifyContent: "space-between",
marginBottom: '1rem', marginBottom: "1rem"
}} }}
> >
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2rem', height: "2rem",
width: '8rem', width: "8rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
marginTop: '1rem', marginTop: "1rem",
height: '1rem', height: "1rem",
width: '8rem', width: "8rem"
}} }}
/> />
</div> </div>
<div <div
className="card__body" className="card__body"
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '3rem', height: "3rem"
}} }}
/> />
<a className="button disabled button--primary button--block">&nbsp;</a> <a className="button disabled button--primary button--block">&nbsp;</a>
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
width: '8rem', width: "8rem",
height: '2rem', height: "2rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
width: '11rem', width: "11rem",
height: '1rem', height: "1rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
width: '9rem', width: "9rem",
height: '1rem', height: "1rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
width: '13rem', width: "13rem",
height: '1rem', height: "1rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
width: '11rem', width: "11rem",
height: '1rem', height: "1rem"
}} }}
/> />
</div> </div>

View file

@ -1,4 +1,4 @@
import React from 'react'; import React from "react";
export const HeaderText = ({ type, text, link }): JSX.Element => { export const HeaderText = ({ type, text, link }): JSX.Element => {
const Header = type; const Header = type;

View file

@ -1,28 +1,28 @@
import React from 'react'; import React from "react";
import { FaAndroid, FaApple } from 'react-icons/fa'; import { FaAndroid, FaApple } from "react-icons/fa";
import useSWR from 'swr'; import useSWR from "swr";
import { import {
ArrowTopRightOnSquareIcon, ArrowTopRightOnSquareIcon,
BoltIcon, BoltIcon,
ComputerDesktopIcon, ComputerDesktopIcon,
CpuChipIcon, CpuChipIcon,
GlobeAltIcon, GlobeAltIcon
} from '@heroicons/react/24/solid'; } from "@heroicons/react/24/solid";
import Layout from '@theme/Layout'; import Layout from "@theme/Layout";
import { FirmwareReleases } from '../../utils/apiTypes.js'; import { FirmwareReleases } from "../../utils/apiTypes.js";
import { fetcher } from '../../utils/swr'; import { fetcher } from "../../utils/swr";
import { import {
FirmwareCard, FirmwareCard,
PlaceholderFirmwareCard, PlaceholderFirmwareCard
} from './_components/FirmwareCard'; } from "./_components/FirmwareCard";
const Firmware = (): JSX.Element => { const Firmware = (): JSX.Element => {
const { data, error } = useSWR<FirmwareReleases>( const { data, error } = useSWR<FirmwareReleases>(
'https://api.meshtastic.org/github/firmware/list', "https://api.meshtastic.org/github/firmware/list",
fetcher, fetcher
); );
return ( return (

View file

@ -9,8 +9,8 @@ sidebar_label: e
<meta property="og:site_name" content="Meshtastic.org" /> <meta property="og:site_name" content="Meshtastic.org" />
</head> </head>
# QR Code Share Channel URL Should be opened in the appropriate app ## QR Code Share Channel URL Should be opened in the appropriate app
## Meshtastic QR Code URL Format For Firmware version 2.0 ### Meshtastic QR Code URL Format For Firmware version 2.0
For Android use the QR code scanner in the app, on Apple devices use the camera to open the QR code URL in the Meshtastic App. For Android use the QR code scanner in the app, on Apple devices use the camera to open the QR code URL in the Meshtastic App.

View file

@ -1,19 +1,19 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { FiPlus } from 'react-icons/fi'; import { FiPlus } from "react-icons/fi";
import { HardwareModal } from '@site/src/components/hardware/HardwareModal'; import { HardwareModal } from "@site/src/components/hardware/HardwareModal";
import { IDevice } from '@site/src/data/device'; import { IDevice } from "@site/src/data/device";
import { HardwareCard } from '../../components/hardware/HardwareCard'; import { HardwareCard } from "../../components/hardware/HardwareCard";
import { PageLayout } from '../../components/PageLayout'; import { PageLayout } from "../../components/PageLayout";
import { heltec } from '../../data/devices/heltec'; import { heltec } from "../../data/devices/heltec";
import { hydra } from '../../data/devices/hydra'; import { hydra } from "../../data/devices/hydra";
import { nano_g1 } from '../../data/devices/nano_g1'; import { nano_g1 } from "../../data/devices/nano_g1";
import { rak19001 } from '../../data/devices/rak19001'; import { rak19001 } from "../../data/devices/rak19001";
import { rak19003 } from '../../data/devices/rak19003'; import { rak19003 } from "../../data/devices/rak19003";
import { tbeam } from '../../data/devices/tbeam'; import { tbeam } from "../../data/devices/tbeam";
import { techo } from '../../data/devices/techo'; import { techo } from "../../data/devices/techo";
const Hardware = (): JSX.Element => { const Hardware = (): JSX.Element => {
const hardware = [tbeam, hydra, rak19003, rak19001, nano_g1, heltec, techo]; const hardware = [tbeam, hydra, rak19003, rak19001, nano_g1, heltec, techo];
@ -31,7 +31,7 @@ const Hardware = (): JSX.Element => {
<a <a
href="#" href="#"
className="border-indigo-500 text-indigo-600" className="border-indigo-500 text-indigo-600"
aria-current={'page'} aria-current={"page"}
> >
Devices Devices
</a> </a>

View file

@ -1,21 +1,21 @@
import 'react-responsive-carousel/lib/styles/carousel.min.css'; // requires a loader import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
import React from 'react'; import React from "react";
import { Carousel } from 'react-responsive-carousel'; import { Carousel } from "react-responsive-carousel";
import Head from '@docusaurus/Head'; import Head from "@docusaurus/Head";
import Link from '@docusaurus/Link'; import Link from "@docusaurus/Link";
import useBaseUrl from '@docusaurus/useBaseUrl'; import useBaseUrl from "@docusaurus/useBaseUrl";
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Layout from '@theme/Layout'; import Layout from "@theme/Layout";
import { SocialCard, SocialCardProps } from '../components/homepage/SocialCard'; import { SocialCard, SocialCardProps } from "../components/homepage/SocialCard";
const features = [ const features = [
{ {
title: 'Radio Mesh Text Messaging', title: "Radio Mesh Text Messaging",
imageUrl: 'img/homepage/messages.svg', imageUrl: "img/homepage/messages.svg",
description: ( description: (
<> <>
Off-grid messaging using inexpensive hardware to create your personal Off-grid messaging using inexpensive hardware to create your personal
@ -23,11 +23,11 @@ const features = [
Communicate kilometers/miles between nodes. Internet-connected relay Communicate kilometers/miles between nodes. Internet-connected relay
nodes enable the conversation to move online too. nodes enable the conversation to move online too.
</> </>
), )
}, },
{ {
title: 'Encryption', title: "Encryption",
imageUrl: 'img/homepage/encryption.svg', imageUrl: "img/homepage/encryption.svg",
description: ( description: (
<> <>
Messages are AES256 encrypted. Only radios supplied with your channel Messages are AES256 encrypted. Only radios supplied with your channel
@ -35,22 +35,22 @@ const features = [
Using multichannel settings you can send encrypted messages on one Using multichannel settings you can send encrypted messages on one
channel and still participate in a default Meshtastic mesh. channel and still participate in a default Meshtastic mesh.
</> </>
), )
}, },
{ {
title: 'Conserve Battery', title: "Conserve Battery",
imageUrl: 'img/homepage/battery.svg', imageUrl: "img/homepage/battery.svg",
description: ( description: (
<> <>
Go for days on end and on a single battery or extend it infinitely with 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 a solar cell. Power management ensures the device will last the duration
of your use. of your use.
</> </>
), )
}, },
{ {
title: 'Extensible', title: "Extensible",
imageUrl: 'img/homepage/extendable.svg', imageUrl: "img/homepage/extendable.svg",
description: ( description: (
<> <>
Create a highly scalable mesh with hardware on a multitude of platforms Create a highly scalable mesh with hardware on a multitude of platforms
@ -58,77 +58,98 @@ const features = [
and produce real-time heatmaps, or maybe decentralized, encrypted and produce real-time heatmaps, or maybe decentralized, encrypted
messaging network, your imagination is the limit. messaging network, your imagination is the limit.
</> </>
), )
}, },
{ {
title: 'Platform Agnostic', title: "Platform Agnostic",
imageUrl: 'img/homepage/platforms.svg', imageUrl: "img/homepage/platforms.svg",
description: ( description: (
<> <>
Meshtastic clients are built or being built for all major desktop and Meshtastic clients are built or being built for all major desktop and
mobile platforms. Linux, Windows, Mac, Android, and iOS are all mobile platforms. Linux, Windows, Mac, Android, and iOS are all
supported or well on their way to being supported. supported or well on their way to being supported.
</> </>
), )
}, },
{ {
title: 'Open Source', title: "Open Source",
imageUrl: 'img/homepage/opensource.svg', imageUrl: "img/homepage/opensource.svg",
description: ( description: (
<> <>
All Meshtastic software is open source. If you want an improvement, All Meshtastic software is open source. If you want an improvement,
submit a pull request or file an issue on Github. Happy coding! submit a pull request or file an issue on Github. Happy coding!
</> </>
), )
}, }
]; ];
const SocialCards: SocialCardProps[] = [ const SocialCards: SocialCardProps[] = [
{ {
color: 'bg-[#5865F2]', color: "bg-[#5865F2]",
link: 'https://discord.com/invite/UQJ5QuM7vq', link: "https://discord.com/invite/UQJ5QuM7vq",
children: ( children: (
<img alt="discord" className="m-auto h-10" src="/img/homepage/Discord-Logo-White.svg" /> <img
), alt="discord"
className="m-auto h-10"
src="/img/homepage/Discord-Logo-White.svg"
/>
)
}, },
{ {
color: 'bg-[#ffffff]', color: "bg-[#ffffff]",
link: 'https://twitter.com/TheMeshtastic', link: "https://twitter.com/TheMeshtastic",
children: ( children: (
<img alt="twitter" className="m-auto h-10" src="/img/homepage/Twitter-logo.svg" /> <img
), alt="twitter"
className="m-auto h-10"
src="/img/homepage/Twitter-logo.svg"
/>
)
}, },
{ {
color: 'bg-[#FF0000]', color: "bg-[#FF0000]",
link: 'https://www.youtube.com/meshtastic', link: "https://www.youtube.com/meshtastic",
children: ( children: (
<img alt="youtube" className="m-auto h-16" src="/img/homepage/YouTube-Logo-White.svg" /> <img
), alt="youtube"
className="m-auto h-16"
src="/img/homepage/YouTube-Logo-White.svg"
/>
)
}, },
{ {
color: 'bg-[#ffffff]', color: "bg-[#ffffff]",
link: 'https://meshtastic.discourse.group', link: "https://meshtastic.discourse.group",
children: ( children: (
<img alt="discourse" <img
alt="discourse"
className="m-auto h-12" className="m-auto h-12"
src="/img/homepage/Discourse-Logo-White.svg" src="/img/homepage/Discourse-Logo-White.svg"
/> />
), )
}, },
{ {
color: 'bg-[#FF4500]', color: "bg-[#FF4500]",
link: 'https://reddit.com/r/meshtastic', link: "https://reddit.com/r/meshtastic",
children: ( children: (
<img alt="reddit" className="m-auto h-20" src="/img/homepage/Reddit-Logo-White.svg" /> <img
), alt="reddit"
className="m-auto h-20"
src="/img/homepage/Reddit-Logo-White.svg"
/>
)
}, },
{ {
color: 'bg-[#ffffff]', color: "bg-[#ffffff]",
link: 'https://github.com/meshtastic', link: "https://github.com/meshtastic",
children: ( children: (
<img alt="github" className="m-auto w-12" src="/img/homepage/GitHub-Logo-White.svg" /> <img
), alt="github"
}, className="m-auto w-12"
src="/img/homepage/GitHub-Logo-White.svg"
/>
)
}
]; ];
function Feature({ imageUrl, title, description }) { function Feature({ imageUrl, title, description }) {
@ -155,7 +176,7 @@ function Home() {
<meta property="og:title" content="Meshtastic" /> <meta property="og:title" content="Meshtastic" />
<meta <meta
property="og:image" property="og:image"
content={useBaseUrl('design/web/social-preview-1200x630.png')} content={useBaseUrl("design/web/social-preview-1200x630.png")}
/> />
<meta <meta
property="og:description" property="og:description"
@ -164,14 +185,14 @@ function Home() {
<meta property="og:url" content="https://meshtastic.org/" /> <meta property="og:url" content="https://meshtastic.org/" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
</Head> </Head>
<header style={{ textAlign: 'center' }} className="hero hero--primary"> <header style={{ textAlign: "center" }} className="hero hero--primary">
<div className="container"> <div className="container">
<h1 className="hero__title"> <h1 className="hero__title">
<img <img
style={{ paddingTop: '2rem', paddingBottom: '2rem' }} style={{ paddingTop: "2rem", paddingBottom: "2rem" }}
alt="Meshtastic Logo" alt="Meshtastic Logo"
className="header__logo" className="header__logo"
src={useBaseUrl('design/typelogo/typelogo.svg')} src={useBaseUrl("design/typelogo/typelogo.svg")}
/> />
</h1> </h1>
<p className="hero__subtitle">{siteConfig.tagline}</p> <p className="hero__subtitle">{siteConfig.tagline}</p>
@ -222,23 +243,23 @@ function Home() {
<ul <ul
className="mx-auto" className="mx-auto"
style={{ style={{
position: 'relative', position: "relative",
display: 'grid', display: "grid",
gap: '1.5rem', gap: "1.5rem",
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
paddingLeft: '0', paddingLeft: "0"
}} }}
> >
<div className="card"> <div className="card">
<div <div
className="card__header" className="card__header"
style={{ display: 'flex', justifyContent: 'space-between' }} style={{ display: "flex", justifyContent: "space-between" }}
> >
<h3>1. Purchase Supported Hardware</h3> <h3>1. Purchase Supported Hardware</h3>
</div> </div>
<div <div
className="card__body" className="card__body"
style={{ display: 'flex', justifyContent: 'center' }} style={{ display: "flex", justifyContent: "center" }}
> >
<p> <p>
Hardware you will want to consider: Hardware you will want to consider:
@ -258,13 +279,13 @@ function Home() {
<div className="card"> <div className="card">
<div <div
className="card__header" className="card__header"
style={{ display: 'flex', justifyContent: 'space-between' }} style={{ display: "flex", justifyContent: "space-between" }}
> >
<h3>2. Flash & Configure Node</h3> <h3>2. Flash & Configure Node</h3>
</div> </div>
<div <div
className="card__body" className="card__body"
style={{ display: 'flex', justifyContent: 'center' }} style={{ display: "flex", justifyContent: "center" }}
> >
<p> <p>
The Meshtastic Flasher application can assist you in flashing The Meshtastic Flasher application can assist you in flashing
@ -275,13 +296,13 @@ function Home() {
<div className="card"> <div className="card">
<div <div
className="card__header" className="card__header"
style={{ display: 'flex', justifyContent: 'space-between' }} style={{ display: "flex", justifyContent: "space-between" }}
> >
<h3>3. Connect to Node</h3> <h3>3. Connect to Node</h3>
</div> </div>
<div <div
className="card__body" className="card__body"
style={{ display: 'flex', justifyContent: 'center' }} style={{ display: "flex", justifyContent: "center" }}
> >
<p> <p>
Applications are available for the following systems: Applications are available for the following systems:

View file

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

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import { ShowcaseTag } from '../../../utils/apiTypes'; import { ShowcaseTag } from "../../../utils/apiTypes";
export interface CardTagsProps { export interface CardTagsProps {
tags: ShowcaseTag[]; tags: ShowcaseTag[];
@ -16,8 +16,8 @@ export const CardTags = ({ tags }: CardTagsProps) => {
key={index} key={index}
style={{ style={{
backgroundColor: color, backgroundColor: color,
marginRight: '0.3rem', marginRight: "0.3rem",
userSelect: 'none', userSelect: "none"
}} }}
> >
{label} {label}

View file

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

View file

@ -1,11 +1,11 @@
import React from 'react'; import React from "react";
import useSWR from 'swr'; import useSWR from "swr";
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { Showcase } from '@site/src/utils/apiTypes'; import { Showcase } from "@site/src/utils/apiTypes";
import { User } from '@site/src/utils/github'; import { User } from "@site/src/utils/github";
import { fetcher } from '@site/src/utils/swr'; import { fetcher } from "@site/src/utils/swr";
interface NetworkProps { interface NetworkProps {
id: string; id: string;
@ -16,12 +16,12 @@ export const Network = ({ id }: NetworkProps): JSX.Element => {
const { data, error } = useSWR<Showcase>( const { data, error } = useSWR<Showcase>(
`${siteConfig.customFields.API_URL}/showcase/${id}`, `${siteConfig.customFields.API_URL}/showcase/${id}`,
fetcher, fetcher
); );
const githubData = useSWR<User>( const githubData = useSWR<User>(
`https://api.github.com/users/${data?.author?.githubUsername}`, `https://api.github.com/users/${data?.author?.githubUsername}`,
fetcher, fetcher
).data; ).data;
return ( return (
@ -48,15 +48,15 @@ export const Network = ({ id }: NetworkProps): JSX.Element => {
<div <div
className="card" className="card"
style={{ style={{
marginLeft: 'auto', marginLeft: "auto",
marginRight: 'auto', marginRight: "auto",
maxWidth: '900px', maxWidth: "900px"
}} }}
> >
<div <div
className="card__header" className="card__header"
style={{ style={{
margin: '8px', margin: "8px"
}} }}
> >
<h2>Bill of Materials</h2> <h2>Bill of Materials</h2>
@ -66,14 +66,14 @@ export const Network = ({ id }: NetworkProps): JSX.Element => {
<div <div
key={index} key={index}
style={{ style={{
borderTop: '2px solid gray', borderTop: "2px solid gray",
display: 'flex', display: "flex"
}} }}
> >
<div <div
style={{ style={{
width: '4rem', width: "4rem",
display: 'flex', display: "flex"
}} }}
> >
<img <img
@ -81,13 +81,13 @@ export const Network = ({ id }: NetworkProps): JSX.Element => {
height="auto" height="auto"
width="100%" width="100%"
style={{ style={{
margin: 'auto', margin: "auto",
padding: '4px', padding: "4px",
display: 'block', display: "block",
maxWidth: '60px', maxWidth: "60px",
maxHeight: '60px', maxHeight: "60px",
width: 'auto', width: "auto",
height: 'auto', height: "auto"
}} }}
/> />
</div> </div>
@ -103,8 +103,8 @@ export const Network = ({ id }: NetworkProps): JSX.Element => {
href={material.url} href={material.url}
className="button button--outline button--secondary" className="button button--outline button--secondary"
style={{ style={{
marginTop: 'auto', marginTop: "auto",
marginBottom: 'auto', marginBottom: "auto"
}} }}
> >
View View
@ -129,74 +129,74 @@ export const PlaceholderNetwork = (): JSX.Element => {
<div <div
className="container" className="container"
style={{ style={{
display: 'flex', display: "flex",
flexDirection: window.innerWidth > 768 ? 'row' : 'column', flexDirection: window.innerWidth > 768 ? "row" : "column",
gap: '2rem', gap: "2rem"
}} }}
> >
<div <div
style={{ style={{
width: window.innerWidth > 768 ? '60%' : '100%', width: window.innerWidth > 768 ? "60%" : "100%"
}} }}
> >
<div <div
className="card" className="card"
style={{ style={{
width: '100%', width: "100%",
animation: 'pulse 2s infinite', animation: "pulse 2s infinite",
transform: 'scale(1)', transform: "scale(1)",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '2rem', gap: "2rem",
padding: '2rem', padding: "2rem"
}} }}
> >
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '4rem', height: "4rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '12rem', height: "12rem"
}} }}
/> />
<div style={{ display: 'flex', gap: '1rem' }}> <div style={{ display: "flex", gap: "1rem" }}>
<div <div
style={{ style={{
borderRadius: '999px', borderRadius: "999px",
backgroundColor: 'gray', backgroundColor: "gray",
height: '4rem', height: "4rem",
width: '4rem', width: "4rem",
minWidth: '4rem', minWidth: "4rem"
}} }}
/> />
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '1rem', gap: "1rem",
width: '100%', width: "100%"
}} }}
> >
<div <div
style={{ style={{
width: '100%', width: "100%",
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '1rem', height: "1rem"
}} }}
/> />
<div <div
style={{ style={{
width: '100%', width: "100%",
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2rem', height: "2rem"
}} }}
/> />
</div> </div>
@ -205,108 +205,108 @@ export const PlaceholderNetwork = (): JSX.Element => {
</div> </div>
<div <div
style={{ style={{
width: window.innerWidth > 768 ? '40%' : '100%', width: window.innerWidth > 768 ? "40%" : "100%"
}} }}
> >
<div <div
className="card" className="card"
style={{ style={{
width: '100%', width: "100%",
animation: 'pulse 2s infinite', animation: "pulse 2s infinite",
transform: 'scale(1)', transform: "scale(1)",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '2rem', gap: "2rem",
padding: '2rem', padding: "2rem"
}} }}
> >
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '12rem', height: "12rem"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2rem', height: "2rem"
}} }}
/> />
<div style={{ display: 'flex', gap: '0.5rem' }}> <div style={{ display: "flex", gap: "0.5rem" }}>
<div <div
style={{ style={{
width: '7rem', width: "7rem",
height: '1.8rem', height: "1.8rem",
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray"
}} }}
/> />
<div <div
style={{ style={{
width: '7rem', width: "7rem",
height: '1.8rem', height: "1.8rem",
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray"
}} }}
/> />
<div <div
style={{ style={{
width: '7rem', width: "7rem",
height: '1.8rem', height: "1.8rem",
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray"
}} }}
/> />
</div> </div>
<div <div
style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }} style={{ display: "flex", flexDirection: "column", gap: "1rem" }}
> >
<div style={{ display: 'flex', gap: '1rem' }}> <div style={{ display: "flex", gap: "1rem" }}>
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2.5rem', height: "2.5rem",
width: '20%', width: "20%"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2.5rem', height: "2.5rem",
width: '60%', width: "60%"
}} }}
/> />
<a <a
className="button disabled button--primary button--block" className="button disabled button--primary button--block"
style={{ width: '20%' }} style={{ width: "20%" }}
> >
&nbsp; &nbsp;
</a> </a>
</div> </div>
<div style={{ display: 'flex', gap: '1rem' }}> <div style={{ display: "flex", gap: "1rem" }}>
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2.5rem', height: "2.5rem",
width: '20%', width: "20%"
}} }}
/> />
<div <div
style={{ style={{
borderRadius: '0.4rem', borderRadius: "0.4rem",
backgroundColor: 'gray', backgroundColor: "gray",
height: '2.5rem', height: "2.5rem",
width: '60%', width: "60%"
}} }}
/> />
<a <a
className="button disabled button--primary button--block" className="button disabled button--primary button--block"
style={{ width: '20%' }} style={{ width: "20%" }}
> >
&nbsp; &nbsp;
</a> </a>

View file

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

View file

@ -1,22 +1,22 @@
import React from 'react'; import React from "react";
import { FiHeart, FiSearch } from 'react-icons/fi'; import { FiHeart, FiSearch } from "react-icons/fi";
import useSWR from 'swr'; import useSWR from "swr";
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { useSelectedTags } from '@site/src/hooks/useSelectedTags'; import { useSelectedTags } from "@site/src/hooks/useSelectedTags";
import { useFilteredNetworks } from '../../../hooks/useFilteredNetworks'; import { useFilteredNetworks } from "../../../hooks/useFilteredNetworks";
import { Showcase } from '../../../utils/apiTypes'; import { Showcase } from "../../../utils/apiTypes";
import { fetcher } from '../../../utils/swr'; import { fetcher } from "../../../utils/swr";
import { NetworkSection } from './NetworkSection'; import { NetworkSection } from "./NetworkSection";
export const Networks = (): JSX.Element => { export const Networks = (): JSX.Element => {
const { siteConfig } = useDocusaurusContext(); const { siteConfig } = useDocusaurusContext();
const { data, error } = useSWR<Showcase[]>( const { data, error } = useSWR<Showcase[]>(
`${siteConfig.customFields.API_URL}/showcase`, `${siteConfig.customFields.API_URL}/showcase`,
fetcher, fetcher
); );
const selectedTags = useSelectedTags(); const selectedTags = useSelectedTags();
@ -32,7 +32,7 @@ export const Networks = (): JSX.Element => {
icon={<FiHeart />} icon={<FiHeart />}
iconColor="rgb(190 24 93)" iconColor="rgb(190 24 93)"
networks={data?.filter((network) => networks={data?.filter((network) =>
network.tags.find((tag) => tag.label === 'Favorite'), network.tags.find((tag) => tag.label === "Favorite")
)} )}
/> />
<NetworkSection title="All networks" networks={data} /> <NetworkSection title="All networks" networks={data} />

View file

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

View file

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

View file

@ -1,19 +1,19 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from "react";
import { fromByteArray, toByteArray } from 'base64-js'; import { fromByteArray, toByteArray } from "base64-js";
import { Protobuf } from '@meshtastic/meshtasticjs'; import { Protobuf } from "@meshtastic/meshtasticjs";
import Layout from '@theme/Layout'; import Layout from "@theme/Layout";
const OEM = (): JSX.Element => { const OEM = (): JSX.Element => {
const [oemAesKey, setOemAesKey] = useState<Uint8Array>(new Uint8Array()); const [oemAesKey, setOemAesKey] = useState<Uint8Array>(new Uint8Array());
const [oemFont, setOemFont] = useState<Protobuf.ScreenFonts>( const [oemFont, setOemFont] = useState<Protobuf.ScreenFonts>(
Protobuf.ScreenFonts.FONT_MEDIUM, Protobuf.ScreenFonts.FONT_MEDIUM
); );
const [oemIconBits, setOemIconBits] = useState<Uint8Array>(new Uint8Array()); const [oemIconBits, setOemIconBits] = useState<Uint8Array>(new Uint8Array());
const [oemIconHeight, setOemIconHeight] = useState<number>(0); const [oemIconHeight, setOemIconHeight] = useState<number>(0);
const [oemIconWidth, setOemIconWidth] = useState<number>(0); const [oemIconWidth, setOemIconWidth] = useState<number>(0);
const [oemText, setOemText] = useState<string>(''); const [oemText, setOemText] = useState<string>("");
const [oemBytes, setOemBytes] = useState<Uint8Array>(new Uint8Array()); const [oemBytes, setOemBytes] = useState<Uint8Array>(new Uint8Array());
const encoder = new TextEncoder(); const encoder = new TextEncoder();
@ -26,14 +26,14 @@ const OEM = (): JSX.Element => {
oemIconBits, oemIconBits,
oemIconHeight, oemIconHeight,
oemIconWidth, oemIconWidth,
oemText, oemText
}), })
); );
}, [oemAesKey, oemFont, oemIconBits, oemIconHeight, oemIconWidth, oemText]); }, [oemAesKey, oemFont, oemIconBits, oemIconHeight, oemIconWidth, oemText]);
const enumOptions = Protobuf.ScreenFonts const enumOptions = Protobuf.ScreenFonts
? Object.entries(Protobuf.ScreenFonts).filter( ? Object.entries(Protobuf.ScreenFonts).filter(
(value) => typeof value[1] === 'number', (value) => typeof value[1] === "number"
) )
: []; : [];
@ -102,8 +102,8 @@ const OEM = (): JSX.Element => {
readFile(e.target.files[0]).then((data) => { readFile(e.target.files[0]).then((data) => {
setOemIconBits( setOemIconBits(
new Uint8Array( new Uint8Array(
data.split(',').map((s) => parseInt(s.trim(), 16)), data.split(",").map((s) => parseInt(s.trim(), 16))
), )
); );
}); });
}} }}
@ -137,7 +137,7 @@ const OEM = (): JSX.Element => {
download="OEM.bin" download="OEM.bin"
onClick={() => { onClick={() => {
const blob = new Blob([oemBytes], { const blob = new Blob([oemBytes], {
type: 'application/octet-stream', type: "application/octet-stream"
}); });
window.open(URL.createObjectURL(blob)); window.open(URL.createObjectURL(blob));
}} }}

View file

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

View file

@ -1,8 +1,7 @@
export const sortBy = <T>(array: T[], getter: (item: T) => unknown): T[] => { export const sortBy = <T>(array: T[], getter: (item: T) => unknown): T[] => {
const sortedArray = [...array]; const sortedArray = [...array];
sortedArray.sort((a, b) => sortedArray.sort((a, b) =>
// @ts-ignore getter(a) > getter(b) ? 1 : getter(b) > getter(a) ? -1 : 0
getter(a) > getter(b) ? 1 : getter(b) > getter(a) ? -1 : 0,
); );
return sortedArray; return sortedArray;
}; };

View file

@ -1,20 +1,22 @@
// trunk-ignore(eslint/no-undef)
module.exports = { module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'], content: ["./src/**/*.{js,jsx,ts,tsx}"],
darkMode: 'class', darkMode: "class",
theme: { theme: {
extend: { extend: {
colors: { colors: {
accent: 'var(--accent)', accent: "var(--accent)",
base: 'var(--base)', base: "var(--base)",
primary: 'var(--primary)', primary: "var(--primary)",
secondary: 'var(--secondary)', secondary: "var(--secondary)",
tertiary: 'var(--tertiary)', tertiary: "var(--tertiary)",
mute: 'var(--mute)', mute: "var(--mute)",
primaryInv: 'var(--primaryInv)', primaryInv: "var(--primaryInv)",
secondaryInv: 'var(--secondaryInv)', secondaryInv: "var(--secondaryInv)",
tertiaryInv: 'var(--tertiaryInv)', tertiaryInv: "var(--tertiaryInv)"
}, }
}, }
}, },
plugins: [require('@tailwindcss/typography')], // trunk-ignore(eslint/no-undef)
plugins: [require("@tailwindcss/typography")]
}; };