mirror of
https://github.com/meshtastic/meshtastic.git
synced 2025-03-05 21:00:08 -08:00
use rome as linter & formatter
This commit is contained in:
parent
e4119e097c
commit
2ee6a40939
|
@ -1,9 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@docusaurus/recommended"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"plugins": ["@docusaurus", "@typescript-eslint"],
|
|
||||||
"root": true
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"trailingComma": "none"
|
|
||||||
}
|
|
17
.trunk/configs/rome.json
Normal file
17
.trunk/configs/rome.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../node_modules/rome/configuration_schema.json",
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentSize": 2
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
name: "preset-default",
|
name: "preset-default",
|
||||||
params: {
|
params: {
|
||||||
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,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,15 +7,17 @@ plugins:
|
||||||
ref: v0.0.8
|
ref: v0.0.8
|
||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
|
disabled:
|
||||||
|
- eslint
|
||||||
|
- prettier
|
||||||
enabled:
|
enabled:
|
||||||
|
- rome@12.0.0
|
||||||
- markdownlint@0.33.0
|
- markdownlint@0.33.0
|
||||||
- actionlint@1.6.22
|
- actionlint@1.6.22
|
||||||
- gitleaks@8.15.2
|
- gitleaks@8.15.2
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- shellcheck@0.9.0
|
- shellcheck@0.9.0
|
||||||
- prettier@2.8.2
|
|
||||||
- shfmt@3.5.0
|
- shfmt@3.5.0
|
||||||
- eslint@8.31.0
|
|
||||||
- svgo@3.0.2
|
- svgo@3.0.2
|
||||||
runtimes:
|
runtimes:
|
||||||
enabled:
|
enabled:
|
||||||
|
|
|
@ -7,7 +7,7 @@ sidebar_label: Power
|
||||||
|
|
||||||
import Tabs from "@theme/Tabs";
|
import Tabs from "@theme/Tabs";
|
||||||
import TabItem from "@theme/TabItem";
|
import TabItem from "@theme/TabItem";
|
||||||
import calculateADC from "@site/src/utils/calculateADC";
|
import calculateADC from "/src/utils/calculateADC";
|
||||||
|
|
||||||
The power config options are: Power Saving, Shutdown after losing power, ADC Multiplier Override Wait Bluetooth Interval, Mesh Super Deep Sleep Timeout, Super Deep Sleep Interval, Light Sleep Interval and Minimum Wake Interval. Power config uses an admin message sending a `Config.Power` protobuf.
|
The power config options are: Power Saving, Shutdown after losing power, ADC Multiplier Override Wait Bluetooth Interval, Mesh Super Deep Sleep Timeout, Super Deep Sleep Interval, Light Sleep Interval and Minimum Wake Interval. Power config uses an admin message sending a `Config.Power` protobuf.
|
||||||
|
|
||||||
|
|
|
@ -4,142 +4,142 @@ 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",
|
||||||
// trunk-ignore(gitleaks/generic-api-key)
|
// trunk-ignore(gitleaks/generic-api-key)
|
||||||
apiKey: "2e4348812173ec7ea6f7879c7032bb21",
|
apiKey: "2e4348812173ec7ea6f7879c7032bb21",
|
||||||
indexName: "meshtastic",
|
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;
|
||||||
|
|
27
package.json
27
package.json
|
@ -11,40 +11,37 @@
|
||||||
"clear": "docusaurus clear"
|
"clear": "docusaurus clear"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@algolia/client-search": "^4.16.0",
|
"@algolia/client-search": "^4.17.0",
|
||||||
"@docusaurus/core": "2.4.0",
|
"@docusaurus/core": "2.4.0",
|
||||||
"@docusaurus/plugin-content-docs": "2.4.0",
|
"@docusaurus/plugin-content-docs": "2.4.0",
|
||||||
"@docusaurus/preset-classic": "2.4.0",
|
"@docusaurus/preset-classic": "2.4.0",
|
||||||
"@docusaurus/theme-mermaid": "^2.4.0",
|
"@docusaurus/theme-mermaid": "^2.4.0",
|
||||||
"@headlessui/react": "^1.7.13",
|
"@headlessui/react": "^1.7.14",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.18",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"@meshtastic/meshtasticjs": "2.1.6-0",
|
"@meshtastic/meshtasticjs": "2.1.9-0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
|
||||||
"@typescript-eslint/parser": "^5.57.0",
|
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"eslint": "^8.37.0",
|
|
||||||
"framer-motion": "^6.5.1",
|
"framer-motion": "^6.5.1",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.23",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.8.0",
|
||||||
"react-responsive-carousel": "^3.2.23",
|
"react-responsive-carousel": "^3.2.23",
|
||||||
"swr": "^2.1.1",
|
"swr": "^2.1.5",
|
||||||
"tailwindcss": "^3.3.1",
|
"tailwindcss": "^3.3.2",
|
||||||
"url-search-params-polyfill": "^8.1.1",
|
"url-search-params-polyfill": "^8.1.1",
|
||||||
"use-breakpoint": "^3.0.7"
|
"use-breakpoint": "^3.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/eslint-plugin": "^2.4.0",
|
|
||||||
"@docusaurus/module-type-aliases": "2.4.0",
|
"@docusaurus/module-type-aliases": "2.4.0",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@tsconfig/docusaurus": "^1.0.7",
|
"@tsconfig/docusaurus": "^1.0.7",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^20.1.3",
|
||||||
"@types/react": "^18.0.31",
|
"@types/react": "^18.2.6",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.2.4",
|
||||||
"typescript": "^5.0.3"
|
"rome": "^12.0.0",
|
||||||
|
"typescript": "^5.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1263
pnpm-lock.yaml
1263
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
12
sidebars.js
12
sidebars.js
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Sidebar: [
|
Sidebar: [
|
||||||
{
|
{
|
||||||
type: "autogenerated",
|
type: "autogenerated",
|
||||||
dirName: "."
|
dirName: ".",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const BatteryCalculator = (): JSX.Element => {
|
export const BatteryCalculator = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<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" />
|
||||||
<input placeholder="Search" />
|
<input placeholder="Search" />
|
||||||
<input placeholder="Search" />
|
<input placeholder="Search" />
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
</div>
|
||||||
</div>
|
<div className="card__footer">
|
||||||
<div className="card__footer">
|
<button
|
||||||
<button className="button button--secondary button--block">
|
type="button"
|
||||||
See All
|
className="button button--secondary button--block"
|
||||||
</button>
|
>
|
||||||
</div>
|
See All
|
||||||
</div>
|
</button>
|
||||||
);
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,14 +3,14 @@ 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"
|
||||||
>
|
>
|
||||||
<div className="m-auto">{children}</div>
|
<div className="m-auto">{children}</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface ColorModeProps {
|
export interface ColorModeProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Dark = ({ children }: ColorModeProps): JSX.Element => {
|
export const Dark = ({ children }: ColorModeProps): JSX.Element => {
|
||||||
return <div className="hideLight">{children}</div>;
|
return <div className="hideLight">{children}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Light = ({ children }: ColorModeProps): JSX.Element => {
|
export const Light = ({ children }: ColorModeProps): JSX.Element => {
|
||||||
return <div className="hideDark">{children}</div>;
|
return <div className="hideDark">{children}</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,45 +5,45 @@ 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;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Modal = ({ open, onClose, children }: ModalProps): JSX.Element => {
|
export const Modal = ({ open, onClose, children }: ModalProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence initial={false} exitBeforeEnter={true}>
|
<AnimatePresence initial={false} exitBeforeEnter={true}>
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="fixed inset-0 z-10 overflow-y-auto"
|
className="fixed inset-0 z-10 overflow-y-auto"
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className="min-h-screen px-0.5 text-center md:px-4">
|
<div className="min-h-screen px-0.5 text-center md:px-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
>
|
>
|
||||||
<Dialog.Overlay className="fixed inset-0 backdrop-blur-md" />
|
<Dialog.Overlay className="fixed inset-0 backdrop-blur-md" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className="inline-block h-screen align-middle"
|
className="inline-block h-screen align-middle"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
​
|
​
|
||||||
</span>
|
</span>
|
||||||
<div className="inline-block w-full transform text-left align-middle transition-all 2xl:max-w-7xl">
|
<div className="inline-block w-full transform text-left align-middle transition-all 2xl:max-w-7xl">
|
||||||
<div className="group relative">
|
<div className="group relative">
|
||||||
<div className="animate-tilt absolute -inset-0.5 rotate-2 rounded-lg bg-accent shadow-md transition duration-1000 group-hover:opacity-100 group-hover:duration-200"></div>
|
<div className="animate-tilt absolute -inset-0.5 rotate-2 rounded-lg bg-accent shadow-md transition duration-1000 group-hover:opacity-100 group-hover:duration-200" />
|
||||||
<div className="relative flex flex-col overflow-hidden rounded-2xl bg-base shadow-md md:aspect-[2/1] md:flex-row md:bg-primary">
|
<div className="relative flex flex-col overflow-hidden rounded-2xl bg-base shadow-md md:aspect-[2/1] md:flex-row md:bg-primary">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,23 +3,23 @@ import React from "react";
|
||||||
import Layout from "@theme/Layout";
|
import Layout from "@theme/Layout";
|
||||||
|
|
||||||
export interface PageLayoutProps {
|
export interface PageLayoutProps {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
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}>
|
||||||
{children}
|
{children}
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ColorModeProps {
|
export interface ColorModeProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface BadgeProps {
|
export interface BadgeProps {
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Badge = ({ name, color, icon }: BadgeProps): JSX.Element => {
|
export const Badge = ({ name, color, icon }: BadgeProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex h-min cursor-pointer gap-1 rounded-md px-1 text-white shadow-md hover:opacity-80 ${color}`}
|
className={`flex h-min cursor-pointer gap-1 rounded-md px-1 text-white shadow-md hover:opacity-80 ${color}`}
|
||||||
>
|
>
|
||||||
<div className="my-1">{icon}</div>
|
<div className="my-1">{icon}</div>
|
||||||
<span className="hidden truncate md:flex">{name}</span>
|
<span className="hidden truncate md:flex">{name}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,19 +3,19 @@ import React from "react";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
|
|
||||||
export interface CardTabProps {
|
export interface CardTabProps {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardTab = ({ title }: CardTabProps): JSX.Element => {
|
export const CardTab = ({ title }: CardTabProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<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" : ""
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Tab>
|
</Tab>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,58 +1,61 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { IDevice, Stability } from "@site/src/data/device";
|
import { IDevice, Stability } from "../../data/device";
|
||||||
|
|
||||||
export interface HardwareCardProps {
|
export interface HardwareCardProps {
|
||||||
device: IDevice;
|
device: IDevice;
|
||||||
setDevice: () => void;
|
setDevice: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HardwareCard = ({
|
export const HardwareCard = ({
|
||||||
device,
|
device,
|
||||||
setDevice
|
setDevice,
|
||||||
}: HardwareCardProps): JSX.Element => {
|
}: HardwareCardProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className="group relative"
|
className="group relative"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDevice();
|
setDevice();
|
||||||
}}
|
}}
|
||||||
>
|
onKeyDown={() => {
|
||||||
<div className="overflow-hidden rounded-lg">
|
setDevice();
|
||||||
<div
|
}}
|
||||||
className={`flex aspect-[4/3] overflow-hidden ${device.misc.Gradient}`}
|
>
|
||||||
>
|
<div className="overflow-hidden rounded-lg">
|
||||||
<img
|
<div
|
||||||
src={device.images.Front}
|
className={`flex aspect-[4/3] overflow-hidden ${device.misc.Gradient}`}
|
||||||
alt=""
|
>
|
||||||
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2 group-hover:opacity-75"
|
<img
|
||||||
/>
|
src={device.images.Front}
|
||||||
</div>
|
alt=""
|
||||||
<button type="button" className="absolute inset-0 focus:outline-none">
|
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2 group-hover:opacity-75"
|
||||||
<span className="sr-only">View details for {device.name}</span>
|
/>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
<button type="button" className="absolute inset-0 focus:outline-none">
|
||||||
<div className="flex">
|
<span className="sr-only">View details for {device.name}</span>
|
||||||
<div>
|
</button>
|
||||||
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
</div>
|
||||||
{device.name}
|
<div className="flex">
|
||||||
</p>
|
<div>
|
||||||
<p className="pointer-events-none flex gap-1 text-sm font-medium text-mute">
|
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
||||||
<div
|
{device.name}
|
||||||
className={`my-auto h-3 w-3 rounded-full ${
|
</p>
|
||||||
device.misc.Stability === Stability.Broken
|
<p className="pointer-events-none flex gap-1 text-sm font-medium text-mute">
|
||||||
? "bg-red-500"
|
<div
|
||||||
: device.misc.Stability === Stability.Unstable
|
className={`my-auto h-3 w-3 rounded-full ${
|
||||||
? "bg-orange-500"
|
device.misc.Stability === Stability.Broken
|
||||||
: device.misc.Stability === Stability.Semi
|
? "bg-red-500"
|
||||||
? "bg-cyan-500"
|
: device.misc.Stability === Stability.Unstable
|
||||||
: "bg-green-500"
|
? "bg-orange-500"
|
||||||
}`}
|
: device.misc.Stability === Stability.Semi
|
||||||
/>
|
? "bg-cyan-500"
|
||||||
<div className="my-auto">{Stability[device.misc.Stability]}</div>
|
: "bg-green-500"
|
||||||
</p>
|
}`}
|
||||||
</div>
|
/>
|
||||||
</div>
|
<div className="my-auto">{Stability[device.misc.Stability]}</div>
|
||||||
</li>
|
</p>
|
||||||
);
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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 "../../data/device";
|
||||||
|
|
||||||
import { Button } from "../../components/Button";
|
import { Button } from "../../components/Button";
|
||||||
import { BREAKPOINTS } from "../../utils/breakpoints";
|
import { BREAKPOINTS } from "../../utils/breakpoints";
|
||||||
|
@ -18,156 +18,156 @@ import { PowerTab } from "./Tabs/PowerTab";
|
||||||
import { VariantSelectButton } from "./VariantSelectButton";
|
import { VariantSelectButton } from "./VariantSelectButton";
|
||||||
|
|
||||||
export interface HardwareModal {
|
export interface HardwareModal {
|
||||||
device: IDevice;
|
device: IDevice;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
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}>
|
||||||
<div className="absolute right-0 z-20 m-2 md:flex">
|
<div className="absolute right-0 z-20 m-2 md:flex">
|
||||||
<Button onClick={close}>
|
<Button onClick={close}>
|
||||||
<FiX />
|
<FiX />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<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"
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
layout
|
layout
|
||||||
className={`relative z-10 flex h-full w-full rounded-t-2xl md:rounded-l-2xl md:rounded-tr-none ${device.misc.Gradient}`}
|
className={`relative z-10 flex h-full w-full rounded-t-2xl md:rounded-l-2xl md:rounded-tr-none ${device.misc.Gradient}`}
|
||||||
>
|
>
|
||||||
<motion.img
|
<motion.img
|
||||||
layout
|
layout
|
||||||
src={device.images.Front}
|
src={device.images.Front}
|
||||||
alt=""
|
alt=""
|
||||||
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2"
|
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2"
|
||||||
/>
|
/>
|
||||||
<div className="absolute -bottom-5 z-20 flex w-full md:bottom-auto md:-right-5 md:h-full md:w-auto">
|
<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);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FiChevronRight />
|
<FiChevronRight />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{!hideDetails && (
|
{!hideDetails && (
|
||||||
<>
|
<>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
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} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
className="absolute -bottom-3 right-0 m-auto mr-2 ml-auto flex md:inset-x-1 md:bottom-4 md:mt-2"
|
className="absolute -bottom-3 right-0 m-auto mr-2 ml-auto flex md:inset-x-1 md:bottom-4 md:mt-2"
|
||||||
>
|
>
|
||||||
<div className="m-auto flex gap-2">
|
<div className="m-auto flex gap-2">
|
||||||
{device.features.BLE && (
|
{device.features.BLE && (
|
||||||
<Badge
|
<Badge
|
||||||
name="Bluetooth"
|
name="Bluetooth"
|
||||||
color="bg-blue-500"
|
color="bg-blue-500"
|
||||||
icon={<FiBluetooth />}
|
icon={<FiBluetooth />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{device.features.WiFi && (
|
{device.features.WiFi && (
|
||||||
<Badge
|
<Badge
|
||||||
name="WiFi"
|
name="WiFi"
|
||||||
color="bg-orange-500"
|
color="bg-orange-500"
|
||||||
icon={<FiWifi />}
|
icon={<FiWifi />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
<div className="z-[1] mt-[25%] flex h-full flex-col md:ml-[20%] md:mt-0 md:w-4/5">
|
<div className="z-[1] mt-[25%] flex h-full flex-col md:ml-[20%] md:mt-0 md:w-4/5">
|
||||||
<div className="z-0 hidden pb-2 md:flex">
|
<div className="z-0 hidden pb-2 md:flex">
|
||||||
<VariantSelectButton options={device.variants} />
|
<VariantSelectButton options={device.variants} />
|
||||||
</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
|
||||||
as="div"
|
as="div"
|
||||||
className="flex flex-grow flex-col rounded-2xl bg-primary p-2"
|
className="flex flex-grow flex-col rounded-2xl bg-primary p-2"
|
||||||
>
|
>
|
||||||
<Tab.List className="flex gap-2">
|
<Tab.List className="flex gap-2">
|
||||||
<CardTab title="Info" />
|
<CardTab title="Info" />
|
||||||
<CardTab title="Power" />
|
<CardTab title="Power" />
|
||||||
<CardTab title="Pinout" />
|
<CardTab title="Pinout" />
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels as="div" className="flex-grow overflow-y-auto">
|
<Tab.Panels as="div" className="flex-grow overflow-y-auto">
|
||||||
<InfoTab device={device} />
|
<InfoTab device={device} />
|
||||||
<PowerTab device={device} />
|
<PowerTab device={device} />
|
||||||
<PinoutTab device={device} />
|
<PinoutTab device={device} />
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,44 +5,44 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InfoTab = ({ device }: InfoTabProps): JSX.Element => {
|
export const InfoTab = ({ device }: InfoTabProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<div className="px-4 py-5 sm:p-0">
|
<div className="px-4 py-5 sm:p-0">
|
||||||
<dl className="sm:divide-y sm:divide-gray-200">
|
<dl className="sm:divide-y sm:divide-gray-200">
|
||||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
||||||
<dt className="text-sm font-medium text-secondaryInv">
|
<dt className="text-sm font-medium text-secondaryInv">
|
||||||
BLE/WiFi Version
|
BLE/WiFi Version
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
||||||
<span className="rounded-md bg-secondary px-0.5">
|
<span className="rounded-md bg-secondary px-0.5">
|
||||||
{device.specifications.BLEVersion}
|
{device.specifications.BLEVersion}
|
||||||
</span>
|
</span>
|
||||||
/
|
/
|
||||||
<span className="rounded-md bg-secondary px-0.5">
|
<span className="rounded-md bg-secondary px-0.5">
|
||||||
{device.specifications.WiFiVersion}
|
{device.specifications.WiFiVersion}
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
|
||||||
<dt className="text-sm font-medium text-secondaryInv">
|
<dt className="text-sm font-medium text-secondaryInv">
|
||||||
BLE/WiFi Antenna
|
BLE/WiFi Antenna
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
|
||||||
<span className="rounded-md bg-secondary px-0.5">
|
<span className="rounded-md bg-secondary px-0.5">
|
||||||
{device.specifications.BLEAntenna}
|
{device.specifications.BLEAntenna}
|
||||||
</span>
|
</span>
|
||||||
/
|
/
|
||||||
<span className="rounded-md bg-secondary px-0.5">
|
<span className="rounded-md bg-secondary px-0.5">
|
||||||
{device.specifications.WiFiAntenna}
|
{device.specifications.WiFiAntenna}
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,32 +5,32 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PinoutTab = ({ device }: PinoutTabProps): JSX.Element => {
|
export const PinoutTab = ({ device }: PinoutTabProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Tab.Panel className="flex">
|
<Tab.Panel className="flex">
|
||||||
<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}
|
||||||
>
|
>
|
||||||
<div className="m-auto h-3 w-3 rounded-full border bg-yellow-500" />
|
<div className="m-auto h-3 w-3 rounded-full border bg-yellow-500" />
|
||||||
<span className="m-auto font-mono text-white">{pin.label}</span>
|
<span className="m-auto font-mono text-white">{pin.label}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,9 +5,9 @@ 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 = (): JSX.Element => {
|
export const PowerTab = (): JSX.Element => {
|
||||||
return <Tab.Panel className="h-32">Content 1</Tab.Panel>;
|
return <Tab.Panel className="h-32">Content 1</Tab.Panel>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,84 +5,84 @@ 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 "../../data/device";
|
||||||
|
|
||||||
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]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Listbox value={selected} onChange={setSelected}>
|
<Listbox value={selected} onChange={setSelected}>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<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"
|
||||||
>
|
>
|
||||||
<span className="block truncate">{selected.name}</span>
|
<span className="block truncate">{selected.name}</span>
|
||||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
<HiSelector
|
<HiSelector
|
||||||
className="h-5 w-5 text-gray-400"
|
className="h-5 w-5 text-gray-400"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</Listbox.Button>
|
</Listbox.Button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
show={open}
|
show={open}
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
leave="transition ease-in duration-100"
|
leave="transition ease-in duration-100"
|
||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-primary py-1 shadow-md ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-primary py-1 shadow-md ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||||
{options.map((variant, index) => (
|
{options.map((variant, index) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
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}
|
||||||
>
|
>
|
||||||
{({ selected, active }) => (
|
{({ selected, active }) => (
|
||||||
<>
|
<>
|
||||||
<span
|
<span
|
||||||
className={`block truncate ${
|
className={`block truncate ${
|
||||||
selected ? "font-semibold" : "font-normal"
|
selected ? "font-semibold" : "font-normal"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{variant.name}
|
{variant.name}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{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" />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Listbox.Option>
|
</Listbox.Option>
|
||||||
))}
|
))}
|
||||||
</Listbox.Options>
|
</Listbox.Options>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Listbox>
|
</Listbox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,29 +3,29 @@ 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;
|
||||||
color: string;
|
color: string;
|
||||||
link: string;
|
link: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SocialCard = ({
|
export const SocialCard = ({
|
||||||
children,
|
children,
|
||||||
color,
|
color,
|
||||||
link
|
link,
|
||||||
}: SocialCardProps): JSX.Element => {
|
}: SocialCardProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`group relative flex h-24 w-36 min-w-max flex-shrink-0 rounded-xl shadow-xl ${color} m-2`}
|
className={`group relative flex h-24 w-36 min-w-max flex-shrink-0 rounded-xl shadow-xl ${color} m-2`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<a
|
<a
|
||||||
className="absolute top-0 left-0 right-0 bottom-0 hidden rounded-xl border border-accent bg-secondary bg-opacity-95 text-2xl shadow-xl group-hover:flex"
|
className="absolute top-0 left-0 right-0 bottom-0 hidden rounded-xl border border-accent bg-secondary bg-opacity-95 text-2xl shadow-xl group-hover:flex"
|
||||||
href={link}
|
href={link}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<FiExternalLink className="m-auto" />
|
<FiExternalLink className="m-auto" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
export type DeepPartial<T> = T extends object
|
export type DeepPartial<T> = T extends object
|
||||||
? {
|
? {
|
||||||
[P in keyof T]?: DeepPartial<T[P]>;
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
}
|
}
|
||||||
: T;
|
: T;
|
||||||
|
|
||||||
export enum UseCase {
|
export enum UseCase {
|
||||||
Solar,
|
Solar = 0,
|
||||||
Router,
|
Router = 1,
|
||||||
Portable
|
Portable = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PinUsage {
|
enum PinUsage {
|
||||||
LoRa,
|
LoRa = 0,
|
||||||
GNSS
|
GNSS = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Pin {
|
export interface Pin {
|
||||||
offset: {
|
offset: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
usage?: PinUsage;
|
usage?: PinUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeviceName = "tbeam" | "techo";
|
export type DeviceName = "tbeam" | "techo";
|
||||||
|
@ -44,51 +44,51 @@ 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 = 0,
|
||||||
Semi,
|
Semi = 1,
|
||||||
Unstable,
|
Unstable = 2,
|
||||||
Broken
|
Broken = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
misc: {
|
misc: {
|
||||||
SuggestedUse: UseCase[];
|
SuggestedUse: UseCase[];
|
||||||
Stability: Stability;
|
Stability: Stability;
|
||||||
Gradient: string;
|
Gradient: string;
|
||||||
pinoutSplit: number;
|
pinoutSplit: number;
|
||||||
};
|
};
|
||||||
images: {
|
images: {
|
||||||
Front: string;
|
Front: string;
|
||||||
Back: string;
|
Back: string;
|
||||||
};
|
};
|
||||||
features: {
|
features: {
|
||||||
BLE: boolean;
|
BLE: boolean;
|
||||||
WiFi: boolean;
|
WiFi: boolean;
|
||||||
Modules: Module[];
|
Modules: Module[];
|
||||||
};
|
};
|
||||||
specifications: {
|
specifications: {
|
||||||
BLEVersion?: BLEVersion;
|
BLEVersion?: BLEVersion;
|
||||||
BLEAntenna?: AntennaType;
|
BLEAntenna?: AntennaType;
|
||||||
WiFiVersion?: string;
|
WiFiVersion?: string;
|
||||||
WiFiAntenna?: AntennaType;
|
WiFiAntenna?: AntennaType;
|
||||||
Chipset: Chipset;
|
Chipset: Chipset;
|
||||||
Driver: SerialAdapter;
|
Driver: SerialAdapter;
|
||||||
GNSS?: GNSSModule;
|
GNSS?: GNSSModule;
|
||||||
FlashSize: number;
|
FlashSize: number;
|
||||||
Frequencies: Frequency[];
|
Frequencies: Frequency[];
|
||||||
LoRa: LORAModule;
|
LoRa: LORAModule;
|
||||||
PSRAM: number;
|
PSRAM: number;
|
||||||
RAM?: number;
|
RAM?: number;
|
||||||
};
|
};
|
||||||
pinout: Pin[];
|
pinout: Pin[];
|
||||||
variants: Variant[];
|
variants: Variant[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { IRegion } from "../region";
|
import { IRegion } from "../region";
|
||||||
|
|
||||||
export const UA_433: IRegion = {
|
export const UA_433: IRegion = {
|
||||||
name: "UA_433",
|
name: "UA_433",
|
||||||
freqStart: 433.0,
|
freqStart: 433.0,
|
||||||
freqEnd: 434.7,
|
freqEnd: 434.7,
|
||||||
dutyCycle: 10,
|
dutyCycle: 10,
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
powerLimit: 10,
|
powerLimit: 10,
|
||||||
audioPermitted: true,
|
audioPermitted: true,
|
||||||
frequencySwitching: false,
|
frequencySwitching: false,
|
||||||
wideLora: false
|
wideLora: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { IRegion } from "../region";
|
import { IRegion } from "../region";
|
||||||
|
|
||||||
export const UA_868: IRegion = {
|
export const UA_868: IRegion = {
|
||||||
name: "UA_868",
|
name: "UA_868",
|
||||||
freqStart: 868.0,
|
freqStart: 868.0,
|
||||||
freqEnd: 868.6,
|
freqEnd: 868.6,
|
||||||
dutyCycle: 1,
|
dutyCycle: 1,
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
powerLimit: 14,
|
powerLimit: 14,
|
||||||
audioPermitted: true,
|
audioPermitted: true,
|
||||||
frequencySwitching: false,
|
frequencySwitching: false,
|
||||||
wideLora: false
|
wideLora: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Showcase } from "../utils/apiTypes.js";
|
import { Showcase } from "../utils/apiTypes";
|
||||||
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;
|
||||||
}
|
}
|
||||||
return showcaseNetworks.filter((showcaseNetwork) => {
|
return showcaseNetworks.filter((showcaseNetwork) => {
|
||||||
if (showcaseNetwork.tags.length === 0) {
|
if (showcaseNetwork.tags.length === 0) {
|
||||||
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),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFilteredNetworks = (networks: Showcase[]) => {
|
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],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,12 +5,12 @@ 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();
|
||||||
const [selectedTags, setSelectedTags] = React.useState<string[]>([]);
|
const [selectedTags, setSelectedTags] = React.useState<string[]>([]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const tags = readSearchTags(location.search);
|
const tags = readSearchTags(location.search);
|
||||||
setSelectedTags(tags);
|
setSelectedTags(tags);
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
return selectedTags;
|
return selectedTags;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,454 +4,454 @@ import { FiTwitter } from "react-icons/fi";
|
||||||
|
|
||||||
import { ChevronRightIcon } from "@heroicons/react/20/solid";
|
import { ChevronRightIcon } from "@heroicons/react/20/solid";
|
||||||
import Layout from "@theme/Layout";
|
import Layout from "@theme/Layout";
|
||||||
import { Dark, Light } from "/src/components/ColorMode";
|
import { Dark, Light } from "../../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">
|
||||||
<div>
|
<div>
|
||||||
<main>
|
<main>
|
||||||
{/* Hero section */}
|
{/* Hero section */}
|
||||||
<div className="overflow-hidden pt-8 sm:pt-12 lg:relative lg:py-24">
|
<div className="overflow-hidden pt-8 sm:pt-12 lg:relative lg:py-24">
|
||||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-2 lg:gap-24 lg:px-8">
|
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-2 lg:gap-24 lg:px-8">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<Dark>
|
<Dark>
|
||||||
<img
|
<img
|
||||||
className="h-11 w-auto"
|
className="h-11 w-auto"
|
||||||
it
|
it
|
||||||
src="/design/logo/svg/Mesh_Logo_White.svg"
|
src="/design/logo/svg/Mesh_Logo_White.svg"
|
||||||
alt="Meshtastic Logo"
|
alt="Meshtastic Logo"
|
||||||
/>
|
/>
|
||||||
</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">
|
||||||
<div>
|
<div>
|
||||||
<a href="#" className="inline-flex space-x-4">
|
<a href="#" className="inline-flex space-x-4">
|
||||||
<span className="rounded bg-rose-50 px-2.5 py-1 text-sm font-semibold text-rose-500">
|
<span className="rounded bg-rose-50 px-2.5 py-1 text-sm font-semibold text-rose-500">
|
||||||
What's new
|
What's new
|
||||||
</span>
|
</span>
|
||||||
<span className="inline-flex items-center space-x-1 text-sm font-medium text-rose-500">
|
<span className="inline-flex items-center space-x-1 text-sm font-medium text-rose-500">
|
||||||
<span>Click to find out</span>
|
<span>Click to find out</span>
|
||||||
<ChevronRightIcon
|
<ChevronRightIcon
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:max-w-xl">
|
<div className="mt-6 sm:max-w-xl">
|
||||||
<h1 className="text-4xl font-bold tracking-tight --ifm-heading-color sm:text-5xl">
|
<h1 className="text-4xl font-bold tracking-tight --ifm-heading-color sm:text-5xl">
|
||||||
Meshtastic 2.0 🎉🎉🎉
|
Meshtastic 2.0 🎉🎉🎉
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-6 text-xl --ifm-font-color-base">
|
<p className="mt-6 text-xl --ifm-font-color-base">
|
||||||
After 9 months in the making, we present to you the next
|
After 9 months in the making, we present to you the next
|
||||||
milestone for the Meshtastic project.
|
milestone for the Meshtastic project.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 sm:w-full sm:max-w-lg">
|
<div className="mt-12 sm:w-full sm:max-w-lg">
|
||||||
<p className="mt-6 mb-4 text-xl --ifm-font-color-base">
|
<p className="mt-6 mb-4 text-xl --ifm-font-color-base">
|
||||||
As a part of the launch event, we are running a number of
|
As a part of the launch event, we are running a number of
|
||||||
giveaways, so jump in and win some prizes.
|
giveaways, so jump in and win some prizes.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<a
|
<a
|
||||||
href="#start"
|
href="#start"
|
||||||
className="flex w-full rounded-md border border-transparent bg-rose-500 px-5 py-3 font-medium text-white shadow hover:bg-rose-600 hover:text-black hover:no-underline focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 sm:px-10"
|
className="flex w-full rounded-md border border-transparent bg-rose-500 px-5 py-3 font-medium text-white shadow hover:bg-rose-600 hover:text-black hover:no-underline focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 sm:px-10"
|
||||||
>
|
>
|
||||||
<span className="m-auto">Find Out More</span>
|
<span className="m-auto">Find Out More</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="flex w-16 rounded-md border border-transparent bg-[#1DA1F2] shadow hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2"
|
className="flex w-16 rounded-md border border-transparent bg-[#1DA1F2] shadow hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
href="https://twitter.com/TheMeshtastic/status/1586933393526333441"
|
href="https://twitter.com/TheMeshtastic/status/1586933393526333441"
|
||||||
>
|
>
|
||||||
<FiTwitter className="m-auto text-white" />
|
<FiTwitter className="m-auto text-white" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="sm:mx-auto sm:max-w-3xl sm:px-6">
|
<div className="sm:mx-auto sm:max-w-3xl sm:px-6">
|
||||||
<div className="py-12 sm:relative sm:mt-12 sm:py-16 lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
<div className="py-12 sm:relative sm:mt-12 sm:py-16 lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
||||||
<div className="hidden sm:block">
|
<div className="hidden sm:block">
|
||||||
<div className="absolute inset-y-0 left-1/2 w-screen rounded-l-3xl bg-gray-50 lg:left-80 lg:right-0 lg:w-full" />
|
<div className="absolute inset-y-0 left-1/2 w-screen rounded-l-3xl bg-gray-50 lg:left-80 lg:right-0 lg:w-full" />
|
||||||
<svg
|
<svg
|
||||||
className="absolute top-8 right-1/2 -mr-3 lg:left-0 lg:m-0"
|
className="absolute top-8 right-1/2 -mr-3 lg:left-0 lg:m-0"
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 404 392"
|
viewBox="0 0 404 392"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<pattern
|
<pattern
|
||||||
id="837c3e70-6c3a-44e6-8854-cc48c737b659"
|
id="837c3e70-6c3a-44e6-8854-cc48c737b659"
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
patternUnits="userSpaceOnUse"
|
patternUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={4}
|
width={4}
|
||||||
height={4}
|
height={4}
|
||||||
className="text-gray-200"
|
className="text-gray-200"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
<rect
|
<rect
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="url(#837c3e70-6c3a-44e6-8854-cc48c737b659)"
|
fill="url(#837c3e70-6c3a-44e6-8854-cc48c737b659)"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative -mr-40 pl-4 sm:mx-auto sm:max-w-3xl sm:px-0 lg:h-full lg:max-w-none lg:pl-12">
|
<div className="relative -mr-40 pl-4 sm:mx-auto sm:max-w-3xl sm:px-0 lg:h-full lg:max-w-none lg:pl-12">
|
||||||
<img
|
<img
|
||||||
className="w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
className="w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||||
src="/2.0/webUI.png"
|
src="/2.0/webUI.png"
|
||||||
alt="Web UI"
|
alt="Web UI"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
className="absolute top-0 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
className="absolute top-0 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||||
src="/2.0/android.jpg"
|
src="/2.0/android.jpg"
|
||||||
alt="Android"
|
alt="Android"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
className="absolute top-0 left-96 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
className="absolute top-0 left-96 m-20 w-full rounded-md shadow-xl ring-1 ring-black ring-opacity-5 lg:h-full lg:w-auto lg:max-w-none"
|
||||||
src="/2.0/ios.png"
|
src="/2.0/ios.png"
|
||||||
alt="IOS"
|
alt="IOS"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Testimonial/stats section */}
|
{/* Testimonial/stats section */}
|
||||||
<div className="relative mt-20">
|
<div className="relative mt-20">
|
||||||
<div className="lg:mx-auto lg:grid lg:max-w-7xl lg:grid-cols-2 lg:items-start lg:gap-24 lg:px-8">
|
<div className="lg:mx-auto lg:grid lg:max-w-7xl lg:grid-cols-2 lg:items-start lg:gap-24 lg:px-8">
|
||||||
<div className="relative sm:py-16 lg:py-0">
|
<div className="relative sm:py-16 lg:py-0">
|
||||||
<div
|
<div
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="hidden sm:block lg:absolute lg:inset-y-0 lg:right-0 lg:w-screen"
|
className="hidden sm:block lg:absolute lg:inset-y-0 lg:right-0 lg:w-screen"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-y-0 right-1/2 w-full rounded-r-3xl bg-gray-50 lg:right-72" />
|
<div className="absolute inset-y-0 right-1/2 w-full rounded-r-3xl bg-gray-50 lg:right-72" />
|
||||||
<svg
|
<svg
|
||||||
className="absolute top-8 left-1/2 -ml-3 lg:-right-8 lg:left-auto lg:top-12"
|
className="absolute top-8 left-1/2 -ml-3 lg:-right-8 lg:left-auto lg:top-12"
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 404 392"
|
viewBox="0 0 404 392"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<pattern
|
<pattern
|
||||||
id="02f20b47-fd69-4224-a62a-4c9de5c763f7"
|
id="02f20b47-fd69-4224-a62a-4c9de5c763f7"
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
patternUnits="userSpaceOnUse"
|
patternUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={4}
|
width={4}
|
||||||
height={4}
|
height={4}
|
||||||
className="text-gray-200"
|
className="text-gray-200"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
<rect
|
<rect
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="url(#02f20b47-fd69-4224-a62a-4c9de5c763f7)"
|
fill="url(#02f20b47-fd69-4224-a62a-4c9de5c763f7)"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-none lg:px-0 lg:py-20">
|
<div className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-none lg:px-0 lg:py-20">
|
||||||
{/* Testimonial card*/}
|
{/* Testimonial card*/}
|
||||||
<div className="relative overflow-hidden rounded-2xl pt-64 pb-10 shadow-xl">
|
<div className="relative overflow-hidden rounded-2xl pt-64 pb-10 shadow-xl">
|
||||||
<img
|
<img
|
||||||
className="absolute inset-0 h-full w-full object-cover"
|
className="absolute inset-0 h-full w-full object-cover"
|
||||||
src="/2.0/background.png"
|
src="/2.0/background.png"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-green-500 mix-blend-multiply" />
|
<div className="absolute inset-0 bg-green-500 mix-blend-multiply" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-green-600 via-green-600 opacity-90" />
|
<div className="absolute inset-0 bg-gradient-to-t from-green-600 via-green-600 opacity-90" />
|
||||||
<div className="relative px-8">
|
<div className="relative px-8">
|
||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
className="h-8 text-white"
|
className="h-8 text-white"
|
||||||
src="/2.0/typelogo.svg"
|
src="/2.0/typelogo.svg"
|
||||||
alt="Meshtastic"
|
alt="Meshtastic"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<blockquote className="mt-8">
|
<blockquote className="mt-8">
|
||||||
<div className="relative text-lg font-medium text-white md:flex-grow">
|
<div className="relative text-lg font-medium text-white md:flex-grow">
|
||||||
<svg
|
<svg
|
||||||
className="absolute top-0 left-0 h-8 w-8 -translate-x-3 -translate-y-2 transform text-rose-400"
|
className="absolute top-0 left-0 h-8 w-8 -translate-x-3 -translate-y-2 transform text-rose-400"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 32 32"
|
viewBox="0 0 32 32"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M9.352 4C4.456 7.456 1 13.12 1 19.36c0 5.088 3.072 8.064 6.624 8.064 3.36 0 5.856-2.688 5.856-5.856 0-3.168-2.208-5.472-5.088-5.472-.576 0-1.344.096-1.536.192.48-3.264 3.552-7.104 6.624-9.024L9.352 4zm16.512 0c-4.8 3.456-8.256 9.12-8.256 15.36 0 5.088 3.072 8.064 6.624 8.064 3.264 0 5.856-2.688 5.856-5.856 0-3.168-2.304-5.472-5.184-5.472-.576 0-1.248.096-1.44.192.48-3.264 3.456-7.104 6.528-9.024L25.864 4z" />
|
<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
|
Meshtastic is the neatest open source project I've
|
||||||
ever seen!
|
ever seen!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer className="mt-4">
|
<footer className="mt-4">
|
||||||
<p className="text-base font-semibold text-rose-200">
|
<p className="text-base font-semibold text-rose-200">
|
||||||
Elvis Presley
|
Elvis Presley
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="start"
|
id="start"
|
||||||
className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:px-0"
|
className="relative mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:px-0"
|
||||||
>
|
>
|
||||||
{/* Content area */}
|
{/* Content area */}
|
||||||
<div className="pt-12 sm:pt-16 lg:pt-20">
|
<div className="pt-12 sm:pt-16 lg:pt-20">
|
||||||
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
||||||
A brief overview of all the changes and improvements
|
A brief overview of all the changes and improvements
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-6 space-y-6 --ifm-font-color-base">
|
<div className="mt-6 space-y-6 --ifm-font-color-base">
|
||||||
<h2>Monumental stuff!</h2>
|
<h2>Monumental stuff!</h2>
|
||||||
<p className="leading-7 --ifm-font-color-base">
|
<p className="leading-7 --ifm-font-color-base">
|
||||||
<li>
|
<li>
|
||||||
Completely new LoRA band plan with faster messaging
|
Completely new LoRA band plan with faster messaging
|
||||||
</li>
|
</li>
|
||||||
<li>Smarter and more reliable mesh routing</li>
|
<li>Smarter and more reliable mesh routing</li>
|
||||||
<li>
|
<li>
|
||||||
Unlimited nodes* (80 Connected at a time, oldest node
|
Unlimited nodes* (80 Connected at a time, oldest node
|
||||||
will be removed when a new node joins the mesh)
|
will be removed when a new node joins the mesh)
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
New messaging additions: waypoints, reactions
|
New messaging additions: waypoints, reactions
|
||||||
(tap-backs), and telemetry
|
(tap-backs), and telemetry
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Improvements for Canned Messages module and CardKB
|
Improvements for Canned Messages module and CardKB
|
||||||
messaging for stand alone communicator devices
|
messaging for stand alone communicator devices
|
||||||
</li>
|
</li>
|
||||||
<li>Sensor, Screen, and Input device auto-detection</li>
|
<li>Sensor, Screen, and Input device auto-detection</li>
|
||||||
<li>New devices supported (6 new targets!)</li>
|
<li>New devices supported (6 new targets!)</li>
|
||||||
<li>
|
<li>
|
||||||
Added over the air bluetooth updates for NRF devices
|
Added over the air bluetooth updates for NRF devices
|
||||||
(RAK-4631)
|
(RAK-4631)
|
||||||
</li>
|
</li>
|
||||||
<li>Ethernet support via RAK-13800</li>
|
<li>Ethernet support via RAK-13800</li>
|
||||||
<li>
|
<li>
|
||||||
Compass improvements for larger screens and
|
Compass improvements for larger screens and
|
||||||
customizations
|
customizations
|
||||||
</li>
|
</li>
|
||||||
</p>
|
</p>
|
||||||
<h2>Nerd stuff!</h2>
|
<h2>Nerd stuff!</h2>
|
||||||
<p className="leading-7 --ifm-font-color-base">
|
<p className="leading-7 --ifm-font-color-base">
|
||||||
<li>New filesystem for ESP32 (LittleFS)</li>
|
<li>New filesystem for ESP32 (LittleFS)</li>
|
||||||
<li>
|
<li>
|
||||||
Upgraded Arduino framework for both NRF52 and ESP32
|
Upgraded Arduino framework for both NRF52 and ESP32
|
||||||
</li>
|
</li>
|
||||||
<li>New bluetooth stack for ESP32 (NimBLE)</li>
|
<li>New bluetooth stack for ESP32 (NimBLE)</li>
|
||||||
<li>Unified GPS stack now using NMEA</li>
|
<li>Unified GPS stack now using NMEA</li>
|
||||||
<li>Support for more I2C sensors</li>
|
<li>Support for more I2C sensors</li>
|
||||||
<li>Support for ATECCA608B Cryptographic Coprocessor</li>
|
<li>Support for ATECCA608B Cryptographic Coprocessor</li>
|
||||||
<li>More Serial module I/O modes</li>
|
<li>More Serial module I/O modes</li>
|
||||||
<li>JSON messages over MQTT</li>
|
<li>JSON messages over MQTT</li>
|
||||||
<li>
|
<li>
|
||||||
Device codebase refactored and optimized in many areas
|
Device codebase refactored and optimized in many areas
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Completely restructured protobufs and configuration
|
Completely restructured protobufs and configuration
|
||||||
</li>
|
</li>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats section */}
|
{/* Stats section */}
|
||||||
<div className="mt-10">
|
<div className="mt-10">
|
||||||
<dl className="grid grid-cols-2 gap-x-4 gap-y-8">
|
<dl className="grid grid-cols-2 gap-x-4 gap-y-8">
|
||||||
{stats.map((stat) => (
|
{stats.map((stat) => (
|
||||||
<div
|
<div
|
||||||
key={stat.label}
|
key={stat.label}
|
||||||
className="border-t-2 border-gray-100 pt-6"
|
className="border-t-2 border-gray-100 pt-6"
|
||||||
>
|
>
|
||||||
<dt className="font-medium --ifm-font-color-base">
|
<dt className="font-medium --ifm-font-color-base">
|
||||||
{stat.label}
|
{stat.label}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="text-3xl font-bold tracking-tight --ifm-heading-color">
|
<dd className="text-3xl font-bold tracking-tight --ifm-heading-color">
|
||||||
{stat.value}
|
{stat.value}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logo cloud section */}
|
{/* Logo cloud section */}
|
||||||
<div className="mt-32">
|
<div className="mt-32">
|
||||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||||
<div className="lg:grid lg:grid-cols-2 lg:items-center lg:gap-24">
|
<div className="lg:grid lg:grid-cols-2 lg:items-center lg:gap-24">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
<h2 className="text-3xl font-bold tracking-tight --ifm-heading-color sm:text-4xl">
|
||||||
All made possible by the amazing companies that support us.
|
All made possible by the amazing companies that support us.
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-6 max-w-3xl text-lg leading-7 --ifm-font-color-base">
|
<p className="mt-6 max-w-3xl text-lg leading-7 --ifm-font-color-base">
|
||||||
Running a project of this scale is no easy feat, without the
|
Running a project of this scale is no easy feat, without the
|
||||||
generosity of many of our vendors and providers, none of
|
generosity of many of our vendors and providers, none of
|
||||||
this would be possible.
|
this would be possible.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 grid grid-cols-2 gap-0.5 md:grid-cols-3 lg:mt-0 lg:grid-cols-2">
|
<div className="mt-12 grid grid-cols-2 gap-0.5 md:grid-cols-3 lg:mt-0 lg:grid-cols-2">
|
||||||
{logos.map((logo) => (
|
{logos.map((logo) => (
|
||||||
<div
|
<div
|
||||||
key={logo.name}
|
key={logo.name}
|
||||||
className="col-span-1 flex justify-center bg-gray-50 py-8 px-8"
|
className="col-span-1 flex justify-center bg-gray-50 py-8 px-8"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="max-h-12"
|
className="max-h-12"
|
||||||
src={logo.url}
|
src={logo.url}
|
||||||
alt={logo.name}
|
alt={logo.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CTA section */}
|
{/* CTA section */}
|
||||||
<div className="relative mt-24 sm:mt-32 sm:py-16">
|
<div className="relative mt-24 sm:mt-32 sm:py-16">
|
||||||
<div aria-hidden="true" className="hidden sm:block">
|
<div aria-hidden="true" className="hidden sm:block">
|
||||||
<div className="absolute inset-y-0 left-0 w-1/2 rounded-r-3xl bg-gray-50" />
|
<div className="absolute inset-y-0 left-0 w-1/2 rounded-r-3xl bg-gray-50" />
|
||||||
<svg
|
<svg
|
||||||
className="absolute top-8 left-1/2 -ml-3"
|
className="absolute top-8 left-1/2 -ml-3"
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 404 392"
|
viewBox="0 0 404 392"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<pattern
|
<pattern
|
||||||
id="8228f071-bcee-4ec8-905a-2a059a2cc4fb"
|
id="8228f071-bcee-4ec8-905a-2a059a2cc4fb"
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
patternUnits="userSpaceOnUse"
|
patternUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
width={4}
|
width={4}
|
||||||
height={4}
|
height={4}
|
||||||
className="text-gray-200"
|
className="text-gray-200"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
<rect
|
<rect
|
||||||
width={404}
|
width={404}
|
||||||
height={392}
|
height={392}
|
||||||
fill="url(#8228f071-bcee-4ec8-905a-2a059a2cc4fb)"
|
fill="url(#8228f071-bcee-4ec8-905a-2a059a2cc4fb)"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
<div className="mx-auto max-w-md px-4 sm:max-w-3xl sm:px-6 lg:max-w-7xl lg:px-8">
|
||||||
<div className="relative overflow-hidden rounded-2xl bg-green-500 px-6 py-10 shadow-xl sm:px-12 sm:py-20">
|
<div className="relative overflow-hidden rounded-2xl bg-green-500 px-6 py-10 shadow-xl sm:px-12 sm:py-20">
|
||||||
<div
|
<div
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="absolute inset-0 -mt-72 sm:-mt-32 md:mt-0"
|
className="absolute inset-0 -mt-72 sm:-mt-32 md:mt-0"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
className="absolute inset-0 h-full w-full"
|
className="absolute inset-0 h-full w-full"
|
||||||
preserveAspectRatio="xMidYMid slice"
|
preserveAspectRatio="xMidYMid slice"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 1463 360"
|
viewBox="0 0 1463 360"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
className="text-green-400 text-opacity-40"
|
className="text-green-400 text-opacity-40"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M-82.673 72l1761.849 472.086-134.327 501.315-1761.85-472.086z"
|
d="M-82.673 72l1761.849 472.086-134.327 501.315-1761.85-472.086z"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
className="text-green-600 text-opacity-40"
|
className="text-green-600 text-opacity-40"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M-217.088 544.086L1544.761 72l134.327 501.316-1761.849 472.086z"
|
d="M-217.088 544.086L1544.761 72l134.327 501.316-1761.849 472.086z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-12">
|
<div className="flex flex-col gap-12">
|
||||||
<div className="sm:text-center">
|
<div className="sm:text-center">
|
||||||
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||||
Congratulations to the winners!
|
Congratulations to the winners!
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TwoPointZero;
|
export default TwoPointZero;
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface avatarProps {
|
export interface avatarProps {
|
||||||
imgUrl: string;
|
imgUrl: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
userName?: string;
|
userName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface avatarLayoutProps {
|
export interface avatarLayoutProps {
|
||||||
list: list[];
|
list: list[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Avatar = ({
|
export const Avatar = ({
|
||||||
imgUrl,
|
imgUrl,
|
||||||
name,
|
name,
|
||||||
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 className="avatar__photo avatar__photo--sm" src={imgUrl} />
|
<img className="avatar__photo avatar__photo--sm" src={imgUrl} />
|
||||||
<div className="avatar__intro">
|
<div className="avatar__intro">
|
||||||
<div className="avatar__name">{name}</div>
|
<div className="avatar__name">{name}</div>
|
||||||
<small className="avatar__subtitle">{description}</small>
|
<small className="avatar__subtitle">{description}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AvatarLayout = ({ list }: avatarLayoutProps): JSX.Element => {
|
export const AvatarLayout = ({ 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(() => {
|
{list.map(() => {
|
||||||
return <Avatar />;
|
return <Avatar />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,161 +5,161 @@ import Link from "@docusaurus/Link";
|
||||||
import { AvatarLayout } from "./_components/Avatar";
|
import { 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
|
Meshtastic is community driven. Thousands of hours have been donated
|
||||||
by volunteers who want to develop this amazing project. Whether
|
by volunteers who want to develop this amazing project. Whether
|
||||||
you've submitted a pull request or triaged a bug in our
|
you've submitted a pull request or triaged a bug in our
|
||||||
Discord/Forum. You've made Meshtastic possible. Thank you for your
|
Discord/Forum. You've made Meshtastic possible. Thank you for your
|
||||||
contributions.
|
contributions.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We would also like to recognize those who have donated financially
|
We would also like to recognize those who have donated financially
|
||||||
to the project. As Meshtastic has grown, we've aquired some ongoing
|
to the project. As Meshtastic has grown, we've aquired some ongoing
|
||||||
costs to keep the project running. Thank you for your generous
|
costs to keep the project running. Thank you for your generous
|
||||||
donations.
|
donations.
|
||||||
</p>
|
</p>
|
||||||
</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>Fiscal Sponsors</h2>
|
<h2>Fiscal Sponsors</h2>
|
||||||
<p>
|
<p>
|
||||||
We have partnered with both the{" "}
|
We have partnered with both the{" "}
|
||||||
<a
|
<a
|
||||||
className="underline"
|
className="underline"
|
||||||
href="https://opencollective.com"
|
href="https://opencollective.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Open Collective
|
Open Collective
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
and the{" "}
|
and the{" "}
|
||||||
<a
|
<a
|
||||||
className="underline"
|
className="underline"
|
||||||
href="https://www.oscollective.org"
|
href="https://www.oscollective.org"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Open Source Collective
|
Open Source Collective
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
to help us with a fiscal management framework and banking needs.
|
to help us with a fiscal management framework and banking needs.
|
||||||
They help support over three thousand open source projects including
|
They help support over three thousand open source projects including
|
||||||
the PHP Foundation, F-Droid, Sonarr, LinuxServer and DarkReader. We
|
the PHP Foundation, F-Droid, Sonarr, LinuxServer and DarkReader. We
|
||||||
are in good hands and good company.
|
are in good hands and good company.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
As with everything we do here, Open Collective provides a fully
|
As with everything we do here, Open Collective provides a fully
|
||||||
transparent framework for our budget and expenses. You can see what
|
transparent framework for our budget and expenses. You can see what
|
||||||
we’re bringing in, who is spending money and where that money is
|
we’re bringing in, who is spending money and where that money is
|
||||||
going{" "}
|
going{" "}
|
||||||
<a
|
<a
|
||||||
className="underline"
|
className="underline"
|
||||||
href="https://opencollective.com/meshtastic"
|
href="https://opencollective.com/meshtastic"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In addition to our partnership with Open Collective and Open Source
|
In addition to our partnership with Open Collective and Open Source
|
||||||
Collective, we have also been approved into the{" "}
|
Collective, we have also been approved into the{" "}
|
||||||
<a
|
<a
|
||||||
className="underline"
|
className="underline"
|
||||||
href="https://github.com/sponsors"
|
href="https://github.com/sponsors"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
GitHub Sponsors
|
GitHub Sponsors
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
program where we can set fundraising goals with GitHub.
|
program where we can set fundraising goals with GitHub.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
All donations made through GitHub will be deposited to our account
|
All donations made through GitHub will be deposited to our account
|
||||||
with the Open Source Collective and managed by the Open Collective.
|
with the Open Source Collective and managed by the Open Collective.
|
||||||
This means we have a single place to monitor and maintain
|
This means we have a single place to monitor and maintain
|
||||||
transparency of our finances.
|
transparency of our finances.
|
||||||
</p>
|
</p>
|
||||||
<p>If you are able, please contribute to this amazing project.</p>
|
<p>If you are able, please contribute to this amazing project.</p>
|
||||||
<div className="indexCtasBody">
|
<div className="indexCtasBody">
|
||||||
<Link
|
<Link
|
||||||
className={"button button--outline button--lg cta--button"}
|
className={"button button--outline button--lg cta--button"}
|
||||||
to={"https://opencollective.com/meshtastic/donate"}
|
to={"https://opencollective.com/meshtastic/donate"}
|
||||||
>
|
>
|
||||||
Sponsor Meshtastic
|
Sponsor Meshtastic
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<h3>
|
<h3>
|
||||||
Open Collective Donations
|
Open Collective Donations
|
||||||
{/*Open Collective Donations*/}
|
{/*Open Collective Donations*/}
|
||||||
<AvatarLayout list={[]} />
|
<AvatarLayout list={[]} />
|
||||||
</h3>
|
</h3>
|
||||||
<h3>
|
<h3>
|
||||||
GitHub Sponsor Donations
|
GitHub Sponsor Donations
|
||||||
{/*GitHub Sponsor Donations*/}
|
{/*GitHub Sponsor Donations*/}
|
||||||
<AvatarLayout list={[]} />
|
<AvatarLayout list={[]} />
|
||||||
</h3>
|
</h3>
|
||||||
</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>Partnerships</h2>
|
||||||
<div className="mt-12 grid grid-cols-2 gap-0.5 md:grid-cols-3 lg:mt-0 lg:grid-cols-2">
|
<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) => (
|
{partnerLogos.map((logo) => (
|
||||||
<div
|
<div
|
||||||
key={logo.name}
|
key={logo.name}
|
||||||
className="col-span-1 flex justify-center bg-gray-50 py-8 px-8"
|
className="col-span-1 flex justify-center bg-gray-50 py-8 px-8"
|
||||||
>
|
>
|
||||||
<img className="max-h-12" src={logo.url} alt={logo.name} />
|
<img className="max-h-12" src={logo.url} alt={logo.name} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>Contributors</h2>
|
<h2>Contributors</h2>
|
||||||
<p>
|
<p>
|
||||||
Literally thousands of hours have gone into creating, maintaining,
|
Literally thousands of hours have gone into creating, maintaining,
|
||||||
and improving Meshtastic. Without our contributors none of this
|
and improving Meshtastic. Without our contributors none of this
|
||||||
would be possible. Thank you for donating the time for each and
|
would be possible. Thank you for donating the time for each and
|
||||||
every commit, issue, and pull request.
|
every commit, issue, and pull request.
|
||||||
</p>
|
</p>
|
||||||
{/*GitHub Organization Contributors*/}
|
{/*GitHub Organization Contributors*/}
|
||||||
<AvatarLayout list={[]} />
|
<AvatarLayout list={[]} />
|
||||||
</div>
|
</div>
|
||||||
{/*Admin Bios*/}
|
{/*Admin Bios*/}
|
||||||
<div className="container mx-auto p-6 leading-normal space-y-4">
|
<div className="container mx-auto p-6 leading-normal space-y-4">
|
||||||
<AvatarLayout list={[]} />
|
<AvatarLayout list={[]} />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Credits;
|
export default Credits;
|
||||||
|
|
|
@ -1,146 +1,146 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export interface downloadCardProps {
|
export interface downloadCardProps {
|
||||||
client: string;
|
client: string;
|
||||||
imgUrl: string;
|
imgUrl: string;
|
||||||
url: string;
|
url: string;
|
||||||
imgUrl2: string;
|
imgUrl2: string;
|
||||||
url2: string;
|
url2: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DownloadCard = ({
|
export const DownloadCard = ({
|
||||||
client,
|
client,
|
||||||
imgUrl,
|
imgUrl,
|
||||||
url,
|
url,
|
||||||
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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PlaceholderCard = (): JSX.Element => {
|
export const PlaceholderCard = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<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"> </a>
|
<a className="button disabled button--primary button--block"> </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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,151 +1,151 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { DeviceFirmwareResource } from "../../../utils/apiTypes.js";
|
import { DeviceFirmwareResource } from "../../../utils/apiTypes";
|
||||||
|
|
||||||
export interface releaseCardProps {
|
export interface releaseCardProps {
|
||||||
variant: string;
|
variant: string;
|
||||||
description: string;
|
description: string;
|
||||||
release?: DeviceFirmwareResource[];
|
release?: DeviceFirmwareResource[];
|
||||||
}
|
}
|
||||||
|
|
||||||
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 && (
|
||||||
<a href={release[0].page_url}>{release[0].title}</a>
|
<a href={release[0].page_url}>{release[0].title}</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<div className="margin-top--sm">
|
<div className="margin-top--sm">
|
||||||
<details>
|
<details>
|
||||||
<summary>Older Versions</summary>
|
<summary>Older Versions</summary>
|
||||||
{release.slice(1, 6).map((release) => {
|
{release.slice(1, 6).map((release) => {
|
||||||
return (
|
return (
|
||||||
<div key={release.id}>
|
<div key={release.id}>
|
||||||
<a href={release.zip_url}>{release.title}</a>
|
<a href={release.zip_url}>{release.title}</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{release?.length ? (
|
{release?.length ? (
|
||||||
<>
|
<>
|
||||||
<a
|
<a
|
||||||
href={release[0].zip_url}
|
href={release[0].zip_url}
|
||||||
className="button button--secondary button--block margin-top--sm"
|
className="button button--secondary button--block margin-top--sm"
|
||||||
>
|
>
|
||||||
Download {variant}
|
Download {variant}
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<button disabled className="button button--secondary button--block">
|
<button disabled className="button button--secondary button--block">
|
||||||
Loading...
|
Loading...
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PlaceholderFirmwareCard = (): JSX.Element => {
|
export const PlaceholderFirmwareCard = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<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"> </a>
|
<a className="button disabled button--primary button--block"> </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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
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;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header className="anchor anchorWithHideOnScrollNavbar_node_modules-@docusaurus-theme-classic-lib-next-theme-Heading-styles-module">
|
<Header className="anchor anchorWithHideOnScrollNavbar_node_modules-@docusaurus-theme-classic-lib-next-theme-Heading-styles-module">
|
||||||
{text}
|
{text}
|
||||||
{link && (
|
{link && (
|
||||||
<a
|
<a
|
||||||
className="hash-link"
|
className="hash-link"
|
||||||
href={`#${link}`}
|
href={`#${link}`}
|
||||||
title="Direct link to heading"
|
title="Direct link to heading"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,236 +4,236 @@ 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";
|
||||||
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 (
|
||||||
<Layout
|
<Layout
|
||||||
title="Downloads"
|
title="Downloads"
|
||||||
description="Downloads for the Meshtastic project"
|
description="Downloads for the Meshtastic project"
|
||||||
>
|
>
|
||||||
<div className="container mt-8 flex flex-col gap-3">
|
<div className="container mt-8 flex flex-col gap-3">
|
||||||
<h1 className="m-2">Flasher</h1>
|
<h1 className="m-2">Flasher</h1>
|
||||||
<div className="flex w-full overflow-hidden rounded-xl">
|
<div className="flex w-full overflow-hidden rounded-xl">
|
||||||
<div className="flex w-1/5 bg-gradient-to-r from-green-500 to-primary">
|
<div className="flex w-1/5 bg-gradient-to-r from-green-500 to-primary">
|
||||||
<BoltIcon className="m-auto h-20" />
|
<BoltIcon className="m-auto h-20" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col bg-primary xl:flex-row">
|
<div className="flex w-full flex-col bg-primary xl:flex-row">
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>Meshtastic Flasher</h3>
|
<h3>Meshtastic Flasher</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<p>Desktop application to flash fimware to your devices.</p>
|
<p>Desktop application to flash fimware to your devices.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/meshtastic/Meshtastic-gui-installer/releases/latest"
|
href="https://github.com/meshtastic/Meshtastic-gui-installer/releases/latest"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
Download
|
Download
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>ESP32 Web Flasher</h3>
|
<h3>ESP32 Web Flasher</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<p>
|
<p>
|
||||||
Web based installer for easy flashing with Chrome and Edge
|
Web based installer for easy flashing with Chrome and Edge
|
||||||
Browser. Works with T-Beam, T-Lora, Nano-G1 and similar
|
Browser. Works with T-Beam, T-Lora, Nano-G1 and similar
|
||||||
boards.
|
boards.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
href="https://flasher.meshtastic.org/"
|
href="https://flasher.meshtastic.org/"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
Go to Flasher
|
Go to Flasher
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>nRF52 Drag & Drop</h3>
|
<h3>nRF52 Drag & Drop</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<p>
|
<p>
|
||||||
Devices such as T-Echo and RAK4631 are flashed via filesystem.
|
Devices such as T-Echo and RAK4631 are flashed via filesystem.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
href="/docs/getting-started/flashing-firmware/nrf52/drag-n-drop"
|
href="/docs/getting-started/flashing-firmware/nrf52/drag-n-drop"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
View Instructions
|
View Instructions
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* */}
|
{/* */}
|
||||||
<h1 className="m-2">Apps</h1>
|
<h1 className="m-2">Apps</h1>
|
||||||
<div className="flex w-full overflow-hidden rounded-xl">
|
<div className="flex w-full overflow-hidden rounded-xl">
|
||||||
<div className="flex w-1/5 bg-gradient-to-r from-rose-500 to-primary">
|
<div className="flex w-1/5 bg-gradient-to-r from-rose-500 to-primary">
|
||||||
<ComputerDesktopIcon className="m-auto h-20" />
|
<ComputerDesktopIcon className="m-auto h-20" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full columns-3 flex-col bg-primary lg:flex-row">
|
<div className="flex w-full columns-3 flex-col bg-primary lg:flex-row">
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>Apple</h3>
|
<h3>Apple</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body flex items-center">
|
<div className="card__body flex items-center">
|
||||||
<div className="m-auto">
|
<div className="m-auto">
|
||||||
<FaApple className="h-20 w-20" />
|
<FaApple className="h-20 w-20" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
Available on MacOS & iOS. Requires MacOS Ventura or iOS 16+.
|
Available on MacOS & iOS. Requires MacOS Ventura or iOS 16+.
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1586432531"
|
href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1586432531"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
App Store
|
App Store
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>Android</h3>
|
<h3>Android</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body flex items-center">
|
<div className="card__body flex items-center">
|
||||||
<div className="m-auto">
|
<div className="m-auto">
|
||||||
<FaAndroid className="h-20 w-20" />
|
<FaAndroid className="h-20 w-20" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">Sideloading also available.</div>
|
<div className="card__body">Sideloading also available.</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://meshtastic.org/docs/software/android/installation"
|
href="https://meshtastic.org/docs/software/android/installation"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
F-Droid
|
F-Droid
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source=downloads-page"
|
href="https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source=downloads-page"
|
||||||
className="mt-4 flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="mt-4 flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
Play Store
|
Play Store
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>Web</h3>
|
<h3>Web</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body flex items-center">
|
<div className="card__body flex items-center">
|
||||||
<div className="m-auto">
|
<div className="m-auto">
|
||||||
<GlobeAltIcon className="h-20 w-20" />
|
<GlobeAltIcon className="h-20 w-20" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
Requires Chromium based browsers.
|
Requires Chromium based browsers.
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://client.meshtastic.org"
|
href="https://client.meshtastic.org"
|
||||||
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
className="m-auto flex rounded-lg border-4 border-transparent bg-accent p-1 font-semibold text-black shadow-md hover:text-black hover:brightness-110 active:border-green-200"
|
||||||
>
|
>
|
||||||
client.meshtastic.org
|
client.meshtastic.org
|
||||||
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
<ArrowTopRightOnSquareIcon className="m-auto ml-2 h-4" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* */}
|
{/* */}
|
||||||
<h1 className="m-2">Firmware</h1>
|
<h1 className="m-2">Firmware</h1>
|
||||||
<div className="flex w-full overflow-hidden rounded-xl">
|
<div className="flex w-full overflow-hidden rounded-xl">
|
||||||
<div className="flex w-1/5 bg-gradient-to-r from-orange-500 to-primary">
|
<div className="flex w-1/5 bg-gradient-to-r from-orange-500 to-primary">
|
||||||
<CpuChipIcon className="m-auto h-20" />
|
<CpuChipIcon className="m-auto h-20" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col bg-primary lg:flex-row">
|
<div className="flex w-full flex-col bg-primary lg:flex-row">
|
||||||
{data && !error ? (
|
{data && !error ? (
|
||||||
<>
|
<>
|
||||||
<FirmwareCard
|
<FirmwareCard
|
||||||
variant="Stable"
|
variant="Stable"
|
||||||
description="Tested feature set. For those who want stability."
|
description="Tested feature set. For those who want stability."
|
||||||
release={data.releases.stable}
|
release={data.releases.stable}
|
||||||
/>
|
/>
|
||||||
<FirmwareCard
|
<FirmwareCard
|
||||||
variant="Alpha"
|
variant="Alpha"
|
||||||
description="Upcoming changes for testing. For those who want new features."
|
description="Upcoming changes for testing. For those who want new features."
|
||||||
release={data.releases.alpha}
|
release={data.releases.alpha}
|
||||||
/>
|
/>
|
||||||
<div className="card m-4 border-2 border-secondary">
|
<div className="card m-4 border-2 border-secondary">
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<h3>Bleeding</h3>
|
<h3>Bleeding</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<p>
|
<p>
|
||||||
Latest successful CI build. For those who want to break
|
Latest successful CI build. For those who want to break
|
||||||
things.
|
things.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer mt-auto">
|
<div className="card__footer mt-auto">
|
||||||
<a
|
<a
|
||||||
href="https://nightly.link/meshtastic/firmware/workflows/main/master/built.zip"
|
href="https://nightly.link/meshtastic/firmware/workflows/main/master/built.zip"
|
||||||
className="button button--secondary button--block"
|
className="button button--secondary button--block"
|
||||||
>
|
>
|
||||||
Download Bleeding
|
Download Bleeding
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<PlaceholderFirmwareCard />
|
<PlaceholderFirmwareCard />
|
||||||
<PlaceholderFirmwareCard />
|
<PlaceholderFirmwareCard />
|
||||||
<PlaceholderFirmwareCard />
|
<PlaceholderFirmwareCard />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Firmware;
|
export default Firmware;
|
||||||
|
|
|
@ -2,8 +2,8 @@ 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 "../../components/hardware/HardwareModal";
|
||||||
import { IDevice } from "@site/src/data/device";
|
import { IDevice } from "../../data/device";
|
||||||
|
|
||||||
import { HardwareCard } from "../../components/hardware/HardwareCard";
|
import { HardwareCard } from "../../components/hardware/HardwareCard";
|
||||||
import { PageLayout } from "../../components/PageLayout";
|
import { PageLayout } from "../../components/PageLayout";
|
||||||
|
@ -16,79 +16,79 @@ 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];
|
||||||
const [modalData, setModalData] = useState<IDevice>();
|
const [modalData, setModalData] = useState<IDevice>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Hardware" description="Supported hardware">
|
<PageLayout title="Hardware" description="Supported hardware">
|
||||||
<div className="border-b border-tertiary p-4">
|
<div className="border-b border-tertiary p-4">
|
||||||
<div className="sm:flex sm:items-baseline">
|
<div className="sm:flex sm:items-baseline">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900">
|
<h3 className="text-lg font-medium leading-6 text-gray-900">
|
||||||
Issues
|
Issues
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-4 sm:mt-0 sm:ml-10">
|
<div className="mt-4 sm:mt-0 sm:ml-10">
|
||||||
<nav className="-mb-px flex space-x-8">
|
<nav className="-mb-px flex space-x-8">
|
||||||
<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>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
className="hover:border-gray-300', 'whitespace-nowrap border-b-2 border-transparent
|
className="hover:border-gray-300', 'whitespace-nowrap border-b-2 border-transparent
|
||||||
px-1 pb-4 text-sm font-medium text-gray-500 hover:text-gray-700"
|
px-1 pb-4 text-sm font-medium text-gray-500 hover:text-gray-700"
|
||||||
>
|
>
|
||||||
Antennas
|
Antennas
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto max-w-7xl py-8 px-4 sm:px-6 lg:px-8">
|
<div className="mx-auto max-w-7xl py-8 px-4 sm:px-6 lg:px-8">
|
||||||
<ul
|
<ul
|
||||||
role="list"
|
role="list"
|
||||||
className="grid grid-cols-2 gap-x-2 gap-y-4 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:grid-cols-5 xl:gap-x-4"
|
className="grid grid-cols-2 gap-x-2 gap-y-4 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:grid-cols-5 xl:gap-x-4"
|
||||||
>
|
>
|
||||||
{hardware.map((device, index) => (
|
{hardware.map((device, index) => (
|
||||||
<HardwareCard
|
<HardwareCard
|
||||||
key={index}
|
key={index}
|
||||||
device={device}
|
device={device}
|
||||||
setDevice={(): void => {
|
setDevice={(): void => {
|
||||||
setModalData(device);
|
setModalData(device);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<li className="group relative">
|
<li className="group relative">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/meshtastic/firmware/issues/new?assignees=&labels=enhancement%2Ctriage&template=New+Board.yml&title=%5BBoard%5D%3A+"
|
href="https://github.com/meshtastic/firmware/issues/new?assignees=&labels=enhancement%2Ctriage&template=New+Board.yml&title=%5BBoard%5D%3A+"
|
||||||
className="flex aspect-[4/3] rounded-lg border-2 border-dashed border-mute group-hover:border-tertiaryInv"
|
className="flex aspect-[4/3] rounded-lg border-2 border-dashed border-mute group-hover:border-tertiaryInv"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<FiPlus className="m-auto h-12 w-12 text-mute group-hover:text-tertiaryInv" />
|
<FiPlus className="m-auto h-12 w-12 text-mute group-hover:text-tertiaryInv" />
|
||||||
</a>
|
</a>
|
||||||
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-primaryInv">
|
||||||
New Board
|
New Board
|
||||||
</p>
|
</p>
|
||||||
<p className="pointer-events-none block text-sm font-medium text-mute">
|
<p className="pointer-events-none block text-sm font-medium text-mute">
|
||||||
Want to support a board?
|
Want to support a board?
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{modalData && (
|
{modalData && (
|
||||||
<HardwareModal
|
<HardwareModal
|
||||||
open={!!modalData}
|
open={!!modalData}
|
||||||
close={() => {
|
close={() => {
|
||||||
setModalData(undefined);
|
setModalData(undefined);
|
||||||
}}
|
}}
|
||||||
device={modalData}
|
device={modalData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Hardware;
|
export default Hardware;
|
||||||
|
|
|
@ -13,299 +13,299 @@ 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
|
||||||
mesh. Radios forward messages to the next to flood the network.
|
mesh. Radios forward messages to the next to flood the network.
|
||||||
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
|
||||||
settings (which includes the key) should be able to read your messages.
|
settings (which includes the key) should be able to read your messages.
|
||||||
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
|
||||||
to fit your unique requirements: Create an environment monitoring mesh
|
to fit your unique requirements: Create an environment monitoring mesh
|
||||||
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/ktMAKGBnBs",
|
link: "https://discord.com/invite/ktMAKGBnBs",
|
||||||
children: (
|
children: (
|
||||||
<img
|
<img
|
||||||
alt="discord"
|
alt="discord"
|
||||||
className="m-auto h-10"
|
className="m-auto h-10"
|
||||||
src="/img/homepage/Discord-Logo-White.svg"
|
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
|
<img
|
||||||
alt="twitter"
|
alt="twitter"
|
||||||
className="m-auto h-10"
|
className="m-auto h-10"
|
||||||
src="/img/homepage/Twitter-logo.svg"
|
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
|
<img
|
||||||
alt="youtube"
|
alt="youtube"
|
||||||
className="m-auto h-16"
|
className="m-auto h-16"
|
||||||
src="/img/homepage/YouTube-Logo-White.svg"
|
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
|
<img
|
||||||
alt="discourse"
|
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
|
<img
|
||||||
alt="reddit"
|
alt="reddit"
|
||||||
className="m-auto h-20"
|
className="m-auto h-20"
|
||||||
src="/img/homepage/Reddit-Logo-White.svg"
|
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
|
<img
|
||||||
alt="github"
|
alt="github"
|
||||||
className="m-auto w-12"
|
className="m-auto w-12"
|
||||||
src="/img/homepage/GitHub-Logo-White.svg"
|
src="/img/homepage/GitHub-Logo-White.svg"
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const context = useDocusaurusContext();
|
const context = useDocusaurusContext();
|
||||||
const { siteConfig } = context;
|
const { siteConfig } = context;
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Head>
|
<Head>
|
||||||
<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"
|
||||||
content="An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices"
|
content="An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices"
|
||||||
/>
|
/>
|
||||||
<meta property="og:url" content="https://meshtastic.org/" />
|
<meta 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>
|
||||||
<div className="indexCtas">
|
<div className="indexCtas">
|
||||||
<Link className="button button--lg" to="/docs/introduction">
|
<Link className="button button--lg" to="/docs/introduction">
|
||||||
Learn More
|
Learn More
|
||||||
</Link>
|
</Link>
|
||||||
<Link className="button button--lg" to="/docs/getting-started">
|
<Link className="button button--lg" to="/docs/getting-started">
|
||||||
Get Started
|
Get Started
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="flex flex-col gap-4">
|
<main className="flex flex-col gap-4">
|
||||||
<Carousel autoPlay infiniteLoop showStatus={false} showThumbs={false}>
|
<Carousel autoPlay infiniteLoop showStatus={false} showThumbs={false}>
|
||||||
{features.map((feature, index) => (
|
{features.map((feature) => (
|
||||||
<div key={index} className="flex p-12">
|
<div key={feature.title} className="flex p-12">
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
<img
|
<img
|
||||||
className="my-auto h-40"
|
className="my-auto h-40"
|
||||||
src={feature.imageUrl}
|
src={feature.imageUrl}
|
||||||
alt={feature.title}
|
alt={feature.title}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="my-auto w-1/2">
|
<div className="my-auto w-1/2">
|
||||||
<h3 className="text-xl font-medium">{feature.title}</h3>
|
<h3 className="text-xl font-medium">{feature.title}</h3>
|
||||||
<p>{feature.description}</p>
|
<p>{feature.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Carousel>
|
</Carousel>
|
||||||
|
|
||||||
<div className="bg-primaryDark mx-auto flex w-full lg:w-auto flex-col gap-4 p-4 shadow-inner">
|
<div className="bg-primaryDark mx-auto flex w-full lg:w-auto flex-col gap-4 p-4 shadow-inner">
|
||||||
<h3 className="text-xl font-bold">Connect with us.</h3>
|
<h3 className="text-xl font-bold">Connect with us.</h3>
|
||||||
<div className="flex w-full overflow-x-auto">
|
<div className="flex w-full overflow-x-auto">
|
||||||
{SocialCards.map((card, index) => (
|
{SocialCards.map((card) => (
|
||||||
<SocialCard key={index} color={card.color} link={card.link}>
|
<SocialCard key={card.link} color={card.color} link={card.link}>
|
||||||
{card.children}
|
{card.children}
|
||||||
</SocialCard>
|
</SocialCard>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container mx-auto flex w-auto flex-col">
|
<div className="container mx-auto flex w-auto flex-col">
|
||||||
<h2 className="mb-2 text-xl font-medium">
|
<h2 className="mb-2 text-xl font-medium">
|
||||||
Getting started with Meshtastic is as easy as 1, 2, 3!
|
Getting started with Meshtastic is as easy as 1, 2, 3!
|
||||||
</h2>
|
</h2>
|
||||||
<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:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Radio</li>
|
<li>Radio</li>
|
||||||
<li>Battery</li>
|
<li>Battery</li>
|
||||||
<li>Case</li>
|
<li>Case</li>
|
||||||
<li>
|
<li>
|
||||||
Antenna (most devices include an antenna, but the quality
|
Antenna (most devices include an antenna, but the quality
|
||||||
can be a bit of a mixed bag from some suppliers on stock
|
can be a bit of a mixed bag from some suppliers on stock
|
||||||
antennas)
|
antennas)
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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
|
||||||
the firmware and configuring settings.
|
the firmware and configuring settings.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Android</li>
|
<li>Android</li>
|
||||||
<li>iOS</li>
|
<li>iOS</li>
|
||||||
<li>Mac</li>
|
<li>Mac</li>
|
||||||
<li>Web Browser</li>
|
<li>Web Browser</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
|
@ -5,106 +5,106 @@ import { mapUrl } from "../../../utils/map";
|
||||||
import { CardTags } from "./CardTags";
|
import { CardTags } from "./CardTags";
|
||||||
|
|
||||||
export interface CardProps {
|
export interface CardProps {
|
||||||
network: Showcase;
|
network: Showcase;
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<h4>{network.title}</h4>
|
<h4>{network.title}</h4>
|
||||||
<small>{network.summary}</small>
|
<small>{network.summary}</small>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__footer">
|
<div className="card__footer">
|
||||||
<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>
|
||||||
<CardTags tags={network.tags} />
|
<CardTags tags={network.tags} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const PlaceholderCard = (): JSX.Element => (
|
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" }}
|
||||||
>
|
>
|
||||||
|
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,27 +3,27 @@ import React from "react";
|
||||||
import { ShowcaseTag } from "../../../utils/apiTypes";
|
import { ShowcaseTag } from "../../../utils/apiTypes";
|
||||||
|
|
||||||
export interface CardTagsProps {
|
export interface CardTagsProps {
|
||||||
tags: ShowcaseTag[];
|
tags: ShowcaseTag[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardTags = ({ tags }: CardTagsProps) => {
|
export const CardTags = ({ tags }: CardTagsProps) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{tags.map(({ color, label }, index) => {
|
{tags.map(({ color, label }, index) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="badge"
|
className="badge"
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
marginRight: "0.3rem",
|
marginRight: "0.3rem",
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,87 +4,87 @@ 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 "../../../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 (
|
||||||
<section className="margin-top--l margin-bottom--lg container">
|
<section className="margin-top--l margin-bottom--lg container">
|
||||||
{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) => {
|
||||||
const { label, color } = tag;
|
const { label, color } = tag;
|
||||||
const id = `showcase_checkbox_id_${tag};`;
|
const id = `showcase_checkbox_id_${tag};`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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
|
||||||
tag={tag}
|
tag={tag}
|
||||||
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 />
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
width: 10,
|
width: 10,
|
||||||
height: 10,
|
height: 10,
|
||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
marginLeft: 8
|
marginLeft: 8,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<PlaceholderTagSelect />
|
<PlaceholderTagSelect />
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,317 +3,316 @@ 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 "../../../utils/apiTypes";
|
||||||
import { User } from "@site/src/utils/github";
|
import { fetcher } from "../../../utils/swr";
|
||||||
import { fetcher } from "@site/src/utils/swr";
|
|
||||||
|
|
||||||
interface NetworkProps {
|
interface NetworkProps {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Network = ({ id }: NetworkProps): JSX.Element => {
|
export const Network = ({ id }: NetworkProps): JSX.Element => {
|
||||||
const { siteConfig } = useDocusaurusContext();
|
const { siteConfig } = useDocusaurusContext();
|
||||||
|
|
||||||
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(
|
||||||
`https://api.github.com/users/${data?.author?.githubUsername}`,
|
`https://api.github.com/users/${data?.author?.githubUsername}`,
|
||||||
fetcher
|
fetcher,
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{data && !error ? (
|
{data && !error ? (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h1>{data.title}</h1>
|
<h1>{data.title}</h1>
|
||||||
<p>{data.summary}</p>
|
<p>{data.summary}</p>
|
||||||
{githubData && (
|
{githubData && (
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
<img
|
<img
|
||||||
src={githubData.avatar_url}
|
src={githubData.avatar_url}
|
||||||
alt={githubData.name}
|
alt={githubData.name}
|
||||||
className="avatar__photo"
|
className="avatar__photo"
|
||||||
/>
|
/>
|
||||||
<div className="avatar__intro">
|
<div className="avatar__intro">
|
||||||
<div className="avatar__name">{githubData.name}</div>
|
<div className="avatar__name">{githubData.name}</div>
|
||||||
<div className="avatar__subtitle">{githubData.bio}</div>
|
<div className="avatar__subtitle">{githubData.bio}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="markdown">{data.body}</div>
|
<div className="markdown">{data.body}</div>
|
||||||
|
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
{data.materials?.map((material, index) => (
|
{data.materials?.map((material, index) => (
|
||||||
<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
|
||||||
src={material.image}
|
src={material.image}
|
||||||
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>
|
||||||
<div className="avatar__intro">
|
<div className="avatar__intro">
|
||||||
<div className="avatar__name">{material.name}</div>
|
<div className="avatar__name">{material.name}</div>
|
||||||
<small className="avatar__subtitle">
|
<small className="avatar__subtitle">
|
||||||
{material.details}
|
{material.details}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
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
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{error && <div>{JSON.stringify(error)}</div>}
|
{error && <div>{JSON.stringify(error)}</div>}
|
||||||
{!data && <PlaceholderNetwork />}
|
{!data && <PlaceholderNetwork />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PlaceholderNetwork = (): JSX.Element => {
|
export const PlaceholderNetwork = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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%" }}
|
||||||
>
|
>
|
||||||
|
|
||||||
</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%" }}
|
||||||
>
|
>
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,64 +4,64 @@ import { Showcase } from "../../../utils/apiTypes";
|
||||||
import { Card, PlaceholderCard } from "./Card";
|
import { Card, PlaceholderCard } from "./Card";
|
||||||
|
|
||||||
interface NetworkSectionProps {
|
interface NetworkSectionProps {
|
||||||
title: string;
|
title: string;
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
iconColor?: string;
|
iconColor?: string;
|
||||||
networks?: Showcase[];
|
networks?: Showcase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetworkSection = ({
|
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}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</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 ? (
|
||||||
<>
|
<>
|
||||||
{networks.map((network) => (
|
{networks.map((network) => (
|
||||||
<Card key={network.title} network={network} />
|
<Card key={network.title} network={network} />
|
||||||
))}
|
))}
|
||||||
{networks.length === 0 && <h2>No result</h2>}
|
{networks.length === 0 && <h2>No result</h2>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<PlaceholderCard />
|
<PlaceholderCard />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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 "../../../hooks/useSelectedTags";
|
||||||
|
|
||||||
import { useFilteredNetworks } from "../../../hooks/useFilteredNetworks";
|
import { useFilteredNetworks } from "../../../hooks/useFilteredNetworks";
|
||||||
import { Showcase } from "../../../utils/apiTypes";
|
import { Showcase } from "../../../utils/apiTypes";
|
||||||
|
@ -12,41 +12,41 @@ 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();
|
||||||
const filteredNetworks = useFilteredNetworks(data ?? []);
|
const filteredNetworks = useFilteredNetworks(data ?? []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="margin-top--lg margin-bottom--xl">
|
<section className="margin-top--lg margin-bottom--xl">
|
||||||
{!error ? (
|
{!error ? (
|
||||||
selectedTags.length === 0 ? (
|
selectedTags.length === 0 ? (
|
||||||
<>
|
<>
|
||||||
<NetworkSection
|
<NetworkSection
|
||||||
title="Our favorites"
|
title="Our favorites"
|
||||||
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} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<NetworkSection
|
<NetworkSection
|
||||||
title="Results"
|
title="Results"
|
||||||
icon={<FiSearch />}
|
icon={<FiSearch />}
|
||||||
networks={filteredNetworks}
|
networks={filteredNetworks}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<div>{JSON.stringify(error)}</div>
|
<div>{JSON.stringify(error)}</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,109 +3,109 @@ 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 "../../../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();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagSelect = React.forwardRef<HTMLLabelElement, Props>(
|
export const TagSelect = React.forwardRef<HTMLLabelElement, Props>(
|
||||||
({ icon, label, tag }) => {
|
({ icon, label, tag }) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [selected, setSelected] = React.useState(false);
|
const [selected, setSelected] = React.useState(false);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const tags = readSearchTags(location.search);
|
const tags = readSearchTags(location.search);
|
||||||
setSelected(tags.includes(tag.label));
|
setSelected(tags.includes(tag.label));
|
||||||
}, [tag, location]);
|
}, [tag, location]);
|
||||||
const toggleTag = React.useCallback(() => {
|
const toggleTag = React.useCallback(() => {
|
||||||
const tags = readSearchTags(location.search);
|
const tags = readSearchTags(location.search);
|
||||||
const newTags = toggleListItem(tags, tag.label);
|
const newTags = toggleListItem(tags, tag.label);
|
||||||
const newSearch = replaceSearchTags(location.search, newTags);
|
const newSearch = replaceSearchTags(location.search, newTags);
|
||||||
history.push({ ...location, search: newSearch });
|
history.push({ ...location, search: newSearch });
|
||||||
}, [tag, location, history]);
|
}, [tag, location, history]);
|
||||||
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();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,26 +10,26 @@ 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
|
||||||
title="Showcase"
|
title="Showcase"
|
||||||
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} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Filters />
|
<Filters />
|
||||||
<Networks />
|
<Networks />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Showcase;
|
export default Showcase;
|
||||||
|
|
|
@ -6,146 +6,146 @@ 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());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOemBytes(
|
setOemBytes(
|
||||||
Protobuf.OEMStore.toBinary({
|
Protobuf.OEMStore.toBinary({
|
||||||
oemAesKey,
|
oemAesKey,
|
||||||
oemFont,
|
oemFont,
|
||||||
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",
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const readFile = (file: File) => {
|
const readFile = (file: File) => {
|
||||||
return new Promise((resolve: (value: string) => void, reject) => {
|
return new Promise((resolve: (value: string) => void, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (res) => {
|
reader.onload = (res) => {
|
||||||
resolve(res.target.result as string);
|
resolve(res.target.result as string);
|
||||||
};
|
};
|
||||||
reader.onerror = (err) => reject(err);
|
reader.onerror = (err) => reject(err);
|
||||||
|
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title="OEM Generator" description="OEM Bin Generator">
|
<Layout title="OEM Generator" description="OEM Bin Generator">
|
||||||
<div className="container mt-8 flex flex-col gap-3">
|
<div className="container mt-8 flex flex-col gap-3">
|
||||||
<span>AES Key</span>
|
<span>AES Key</span>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const key = new Uint8Array(128 / 8);
|
const key = new Uint8Array(128 / 8);
|
||||||
setOemAesKey(crypto.getRandomValues(key));
|
setOemAesKey(crypto.getRandomValues(key));
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||||
>
|
>
|
||||||
Generate 128bit
|
Generate 128bit
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const key = new Uint8Array(256 / 8);
|
const key = new Uint8Array(256 / 8);
|
||||||
setOemAesKey(crypto.getRandomValues(key));
|
setOemAesKey(crypto.getRandomValues(key));
|
||||||
}}
|
}}
|
||||||
className="mr-auto cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
className="mr-auto cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||||
>
|
>
|
||||||
Generate 256bit
|
Generate 256bit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="oemAesKey"
|
name="oemAesKey"
|
||||||
value={fromByteArray(oemAesKey)}
|
value={fromByteArray(oemAesKey)}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setOemAesKey(toByteArray(e.target.value));
|
setOemAesKey(toByteArray(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>Font</span>
|
<span>Font</span>
|
||||||
<select
|
<select
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setOemFont(parseInt(e.target.value));
|
setOemFont(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{enumOptions.map(([name, value], index) => (
|
{enumOptions.map(([name, value], index) => (
|
||||||
<option key={index} value={value}>
|
<option key={index} value={value}>
|
||||||
{name}
|
{name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<span>Logo XBM</span>
|
<span>Logo XBM</span>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
name="file"
|
name="file"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
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)),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>Logo Height</span>
|
<span>Logo Height</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="oemIconHeight"
|
name="oemIconHeight"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setOemIconHeight(parseInt(e.target.value));
|
setOemIconHeight(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>Logo Width</span>
|
<span>Logo Width</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="oemIconWidth"
|
name="oemIconWidth"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setOemIconWidth(parseInt(e.target.value));
|
setOemIconWidth(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>Boot Text</span>
|
<span>Boot Text</span>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="oemText"
|
name="oemText"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setOemText(e.target.value);
|
setOemText(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||||
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));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
{oemBytes.toString()}
|
{oemBytes.toString()}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OEM;
|
export default OEM;
|
||||||
|
|
|
@ -1,65 +1,65 @@
|
||||||
export interface Showcase {
|
export interface Showcase {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
body: string;
|
body: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
||||||
tags: ShowcaseTag[];
|
tags: ShowcaseTag[];
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
materials?: Material[];
|
materials?: Material[];
|
||||||
author?: Author;
|
author?: Author;
|
||||||
authorId?: string;
|
authorId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShowcaseTag {
|
export interface ShowcaseTag {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
|
||||||
showcases?: Showcase[];
|
showcases?: Showcase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
id: string;
|
id: string;
|
||||||
latitude: string;
|
latitude: string;
|
||||||
longitude: string;
|
longitude: string;
|
||||||
|
|
||||||
showcase?: Showcase;
|
showcase?: Showcase;
|
||||||
showcaseId?: string;
|
showcaseId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Material {
|
export interface Material {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
details: string;
|
details: string;
|
||||||
image: string;
|
image: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
showcases?: Showcase[];
|
showcases?: Showcase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Author {
|
export interface Author {
|
||||||
id: string;
|
id: string;
|
||||||
githubUsername: string;
|
githubUsername: string;
|
||||||
bio: string;
|
bio: string;
|
||||||
|
|
||||||
showcase?: Showcase[];
|
showcase?: Showcase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceFirmwareResource {
|
export interface DeviceFirmwareResource {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
page_url?: string;
|
page_url?: string;
|
||||||
zip_url?: string;
|
zip_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FirmwareReleases {
|
export interface FirmwareReleases {
|
||||||
releases: {
|
releases: {
|
||||||
stable: DeviceFirmwareResource[];
|
stable: DeviceFirmwareResource[];
|
||||||
alpha: DeviceFirmwareResource[];
|
alpha: DeviceFirmwareResource[];
|
||||||
};
|
};
|
||||||
pullRequests: DeviceFirmwareResource[];
|
pullRequests: DeviceFirmwareResource[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
export default function calculateADC() {
|
export default function calculateADC() {
|
||||||
//const variables
|
//const variables
|
||||||
const BAT_MILLIVOLTS_FULL = 4.2;
|
const BAT_MILLIVOLTS_FULL = 4.2;
|
||||||
const BAT_MILLIVOLTS_EMPTY = 3.27;
|
const BAT_MILLIVOLTS_EMPTY = 3.27;
|
||||||
const BAT_FULL_PERCENT = 1;
|
const BAT_FULL_PERCENT = 1;
|
||||||
//variable
|
//variable
|
||||||
const batteryChargePercent =
|
const batteryChargePercent =
|
||||||
parseFloat(
|
parseFloat(
|
||||||
(<HTMLInputElement>document.getElementById("batteryChargePercent")).value
|
(<HTMLInputElement>document.getElementById("batteryChargePercent")).value,
|
||||||
) / 100;
|
) / 100;
|
||||||
const operativeAdcMultiplier = parseFloat(
|
const operativeAdcMultiplier = parseFloat(
|
||||||
(<HTMLInputElement>document.getElementById("operativeAdcMultiplier")).value
|
(<HTMLInputElement>document.getElementById("operativeAdcMultiplier")).value,
|
||||||
);
|
);
|
||||||
const result =
|
const result =
|
||||||
(operativeAdcMultiplier *
|
(operativeAdcMultiplier *
|
||||||
((BAT_FULL_PERCENT - 1) * BAT_MILLIVOLTS_EMPTY -
|
((BAT_FULL_PERCENT - 1) * BAT_MILLIVOLTS_EMPTY -
|
||||||
BAT_FULL_PERCENT * BAT_MILLIVOLTS_FULL)) /
|
BAT_FULL_PERCENT * BAT_MILLIVOLTS_FULL)) /
|
||||||
((batteryChargePercent - 1) * BAT_MILLIVOLTS_EMPTY -
|
((batteryChargePercent - 1) * BAT_MILLIVOLTS_EMPTY -
|
||||||
batteryChargePercent * BAT_MILLIVOLTS_FULL);
|
batteryChargePercent * BAT_MILLIVOLTS_FULL);
|
||||||
(<HTMLInputElement>(
|
(<HTMLInputElement>(
|
||||||
document.getElementById("newOperativeAdcMultiplier")
|
document.getElementById("newOperativeAdcMultiplier")
|
||||||
)).value = result.toFixed(4);
|
)).value = result.toFixed(4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Node } from "./apiTypes.js";
|
import { Node } from "./apiTypes";
|
||||||
|
|
||||||
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}`;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
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) =>
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const difference = <T>(...arrays: T[][]): T[] => {
|
export const difference = <T>(...arrays: T[][]): T[] => {
|
||||||
return arrays.reduce((a, b) => a.filter((c) => !b.includes(c)));
|
return arrays.reduce((a, b) => a.filter((c) => !b.includes(c)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggleListItem = <T>(list: T[], item: T): T[] => {
|
export const toggleListItem = <T>(list: T[], item: T): T[] => {
|
||||||
const itemIndex = list.indexOf(item);
|
const itemIndex = list.indexOf(item);
|
||||||
if (itemIndex === -1) {
|
if (itemIndex === -1) {
|
||||||
return list.concat(item);
|
return list.concat(item);
|
||||||
} else {
|
} else {
|
||||||
const newList = [...list];
|
const newList = [...list];
|
||||||
newList.splice(itemIndex, 1);
|
newList.splice(itemIndex, 1);
|
||||||
return newList;
|
return newList;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
// trunk-ignore(eslint/no-undef)
|
|
||||||
module.exports = {
|
|
||||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
|
||||||
darkMode: "class",
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
accent: "var(--accent)",
|
|
||||||
base: "var(--base)",
|
|
||||||
primary: "var(--primary)",
|
|
||||||
secondary: "var(--secondary)",
|
|
||||||
tertiary: "var(--tertiary)",
|
|
||||||
mute: "var(--mute)",
|
|
||||||
primaryInv: "var(--primaryInv)",
|
|
||||||
secondaryInv: "var(--secondaryInv)",
|
|
||||||
tertiaryInv: "var(--tertiaryInv)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// trunk-ignore(eslint/no-undef)
|
|
||||||
plugins: [require("@tailwindcss/typography")]
|
|
||||||
};
|
|
23
tailwind.config.ts
Normal file
23
tailwind.config.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
import typography from "@tailwindcss/typography";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||||
|
darkMode: "class",
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
accent: "var(--accent)",
|
||||||
|
base: "var(--base)",
|
||||||
|
primary: "var(--primary)",
|
||||||
|
secondary: "var(--secondary)",
|
||||||
|
tertiary: "var(--tertiary)",
|
||||||
|
mute: "var(--mute)",
|
||||||
|
primaryInv: "var(--primaryInv)",
|
||||||
|
secondaryInv: "var(--secondaryInv)",
|
||||||
|
tertiaryInv: "var(--tertiaryInv)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [typography()],
|
||||||
|
} satisfies Config;
|
Loading…
Reference in a new issue