Compare commits
51
Commits
pre-release
...
v2.5.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72fc3ea337 | ||
|
|
82f4784107 | ||
|
|
4f9fb9976d | ||
|
|
42068ad3d8 | ||
|
|
62f8c4509e | ||
|
|
d699764546 | ||
|
|
8549d56c21 | ||
|
|
4b532fc7f8 | ||
|
|
06d2c393ce | ||
|
|
cecdf9758b | ||
|
|
02cb4f2584 | ||
|
|
8cfcd7b1af | ||
|
|
c0cb059f52 | ||
|
|
a2a9b37238 | ||
|
|
57d0d27bbb | ||
|
|
0e92dd9bea | ||
|
|
c16ebf3917 | ||
|
|
3d3a08a23f | ||
|
|
4d1227a942 | ||
|
|
a8ee273b24 | ||
|
|
3ee7a57480 | ||
|
|
2f2c777c56 | ||
|
|
2f36118e9d | ||
|
|
a6d161581f | ||
|
|
d05ea5a2cc | ||
|
|
471db94242 | ||
|
|
2654e4fbc9 | ||
|
|
f2aa5bfbee | ||
|
|
3b018b0c70 | ||
|
|
921db10d91 | ||
|
|
bf4f593e3a | ||
|
|
1e061a1e19 | ||
|
|
9b9f537e2c | ||
|
|
985cce0b0d | ||
|
|
3fe38eb506 | ||
|
|
51081d3052 | ||
|
|
c08f6d16bb | ||
|
|
62ad4c49f8 | ||
|
|
3b0a1e6108 | ||
|
|
c2f2205626 | ||
|
|
87c729d694 | ||
|
|
8e4f60edf3 | ||
|
|
8811eee9f5 | ||
|
|
2af93f1acd | ||
|
|
78a35544c7 | ||
|
|
3ad2d650b0 | ||
|
|
bf425a8ec7 | ||
|
|
a7d0d36086 | ||
|
|
8ed3ce8203 | ||
|
|
ebd5a3d3a6 | ||
|
|
1cdf18747d |
@@ -27,6 +27,12 @@ jobs:
|
|||||||
- name: Package Output
|
- name: Package Output
|
||||||
run: pnpm package
|
run: pnpm package
|
||||||
|
|
||||||
|
- name: Archive compressed build
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build
|
||||||
|
path: dist/build.tar
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
|||||||
+11
-10
@@ -5,11 +5,12 @@
|
|||||||
"description": "Meshtastic web client",
|
"description": "Meshtastic web client",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"build": "rsbuild build",
|
||||||
"build": "tsc && pnpm check && vite build ",
|
|
||||||
"check": "biome check .",
|
"check": "biome check .",
|
||||||
"check:fix": "pnpm check --write",
|
"check:fix": "pnpm check --write",
|
||||||
"preview": "vite preview",
|
"dev": "rsbuild dev --open",
|
||||||
|
"format": "biome format --write",
|
||||||
|
"preview": "rsbuild preview",
|
||||||
"package": "gzipper c -i html,js,css,png,ico,svg,webmanifest,txt dist dist/output && tar -cvf dist/build.tar -C ./dist/output/ $(ls ./dist/output/)"
|
"package": "gzipper c -i html,js,css,png,ico,svg,webmanifest,txt dist dist/output && tar -cvf dist/build.tar -C ./dist/output/ $(ls ./dist/output/)"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^1.10.0",
|
"@bufbuild/protobuf": "^1.10.0",
|
||||||
"@emeraldpay/hashicon-react": "^0.5.2",
|
"@emeraldpay/hashicon-react": "^0.5.2",
|
||||||
"@meshtastic/js": "2.3.7-1",
|
"@meshtastic/js": "2.3.7-5",
|
||||||
"@noble/curves": "^1.5.0",
|
"@noble/curves": "^1.5.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-checkbox": "^1.1.0",
|
"@radix-ui/react-checkbox": "^1.1.0",
|
||||||
@@ -49,7 +50,7 @@
|
|||||||
"crypto-random-string": "^5.0.0",
|
"crypto-random-string": "^5.0.0",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"lucide-react": "^0.363.0",
|
"lucide-react": "^0.363.0",
|
||||||
"mapbox-gl": "npm:empty-npm-package@^1.0.0",
|
"mapbox-gl": "^3.6.0",
|
||||||
"maplibre-gl": "4.1.2",
|
"maplibre-gl": "4.1.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -60,18 +61,20 @@
|
|||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.3.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"timeago-react": "^3.0.6",
|
"timeago-react": "^3.0.6",
|
||||||
|
"vite-plugin-node-polyfills": "^0.22.0",
|
||||||
"zustand": "4.5.2"
|
"zustand": "4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.8.2",
|
"@biomejs/biome": "^1.8.2",
|
||||||
"@buf/meshtastic_protobufs.bufbuild_es": "1.10.0-20240820152623-fac6975bbc78.1",
|
"@buf/meshtastic_protobufs.bufbuild_es": "1.10.0-20240906232734-3da561588c55.1",
|
||||||
|
"@rsbuild/core": "^1.0.10",
|
||||||
|
"@rsbuild/plugin-react": "^1.0.3",
|
||||||
"@types/chrome": "^0.0.263",
|
"@types/chrome": "^0.0.263",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.9",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/w3c-web-serial": "^1.0.6",
|
"@types/w3c-web-serial": "^1.0.6",
|
||||||
"@types/web-bluetooth": "^0.0.20",
|
"@types/web-bluetooth": "^0.0.20",
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"gzipper": "^7.2.0",
|
"gzipper": "^7.2.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@@ -79,8 +82,6 @@
|
|||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"typescript": "^5.5.2",
|
"typescript": "^5.5.2"
|
||||||
"vite": "^5.3.1",
|
|
||||||
"vite-plugin-environment": "^1.1.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+971
-453
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
|||||||
|
import { defineConfig } from "@rsbuild/core";
|
||||||
|
import { pluginReact } from "@rsbuild/plugin-react";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
|
let hash = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
hash = execSync("git rev-parse --short HEAD").toString().trim();
|
||||||
|
} catch (error) {
|
||||||
|
hash = "DEV";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [pluginReact()],
|
||||||
|
source: {
|
||||||
|
define: {
|
||||||
|
"process.env.COMMIT_HASH": JSON.stringify(hash),
|
||||||
|
},
|
||||||
|
alias: {
|
||||||
|
"@app": "./src",
|
||||||
|
"@pages": "./src/pages",
|
||||||
|
"@components": "./src/components",
|
||||||
|
"@core": "./src/core",
|
||||||
|
"@layouts": "./src/layouts",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
html: {
|
||||||
|
title: "Meshtastic Web",
|
||||||
|
},
|
||||||
|
});
|
||||||
+12
-12
@@ -1,15 +1,15 @@
|
|||||||
import { DeviceWrapper } from "@app/DeviceWrapper.js";
|
import { DeviceWrapper } from "@app/DeviceWrapper.tsx";
|
||||||
import { PageRouter } from "@app/PageRouter.js";
|
import { PageRouter } from "@app/PageRouter.tsx";
|
||||||
import { CommandPalette } from "@components/CommandPalette.js";
|
import { CommandPalette } from "@components/CommandPalette.tsx";
|
||||||
import { DeviceSelector } from "@components/DeviceSelector.js";
|
import { DeviceSelector } from "@components/DeviceSelector.tsx";
|
||||||
import { DialogManager } from "@components/Dialog/DialogManager.js";
|
import { DialogManager } from "@components/Dialog/DialogManager.tsx";
|
||||||
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.js";
|
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.tsx";
|
||||||
import { Toaster } from "@components/Toaster.js";
|
import { Toaster } from "@components/Toaster.tsx";
|
||||||
import Footer from "@components/UI/Footer.js";
|
import Footer from "@components/UI/Footer.tsx";
|
||||||
import { ThemeController } from "@components/generic/ThemeController.js";
|
import { ThemeController } from "@components/generic/ThemeController.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { Dashboard } from "@pages/Dashboard/index.js";
|
import { Dashboard } from "@pages/Dashboard/index.tsx";
|
||||||
import { MapProvider } from "react-map-gl";
|
import { MapProvider } from "react-map-gl";
|
||||||
|
|
||||||
export const App = (): JSX.Element => {
|
export const App = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DeviceContext } from "@core/stores/deviceStore.js";
|
import { DeviceContext } from "@core/stores/deviceStore.ts";
|
||||||
import type { Device } from "@core/stores/deviceStore.js";
|
import type { Device } from "@core/stores/deviceStore.ts";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
export interface DeviceWrapperProps {
|
export interface DeviceWrapperProps {
|
||||||
|
|||||||
+6
-6
@@ -1,9 +1,9 @@
|
|||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { ChannelsPage } from "@pages/Channels.js";
|
import { ChannelsPage } from "@pages/Channels.tsx";
|
||||||
import { ConfigPage } from "@pages/Config/index.js";
|
import { ConfigPage } from "@pages/Config/index.tsx";
|
||||||
import { MapPage } from "@pages/Map.js";
|
import { MapPage } from "@pages/Map.tsx";
|
||||||
import { MessagesPage } from "@pages/Messages.js";
|
import { MessagesPage } from "@pages/Messages.tsx";
|
||||||
import { NodesPage } from "@pages/Nodes.js";
|
import { NodesPage } from "@pages/Nodes.tsx";
|
||||||
|
|
||||||
export const PageRouter = (): JSX.Element => {
|
export const PageRouter = (): JSX.Element => {
|
||||||
const { activePage } = useDevice();
|
const { activePage } = useDevice();
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import {
|
|||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@components/UI/Command.js";
|
} from "@components/UI/Command.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { useCommandState } from "cmdk";
|
import { useCommandState } from "cmdk";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { DeviceSelectorButton } from "@components/DeviceSelectorButton.js";
|
import { DeviceSelectorButton } from "@components/DeviceSelectorButton.tsx";
|
||||||
import { Separator } from "@components/UI/Seperator.js";
|
import { Separator } from "@components/UI/Seperator.tsx";
|
||||||
import { Code } from "@components/UI/Typography/Code.js";
|
import { Code } from "@components/UI/Typography/Code.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.js";
|
import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.tsx";
|
||||||
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.js";
|
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.tsx";
|
||||||
import { ImportDialog } from "@components/Dialog/ImportDialog.js";
|
import { ImportDialog } from "@components/Dialog/ImportDialog.tsx";
|
||||||
import { QRDialog } from "@components/Dialog/QRDialog.js";
|
import { QRDialog } from "@components/Dialog/QRDialog.tsx";
|
||||||
import { RebootDialog } from "@components/Dialog/RebootDialog.js";
|
import { RebootDialog } from "@components/Dialog/RebootDialog.tsx";
|
||||||
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.js";
|
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
|
|
||||||
export const DialogManager = (): JSX.Element => {
|
export const DialogManager = (): JSX.Element => {
|
||||||
const { channels, config, dialog, setDialogOpen } = useDevice();
|
const { channels, config, dialog, setDialogOpen } = useDevice();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Checkbox } from "@components/UI/Checkbox.js";
|
import { Checkbox } from "@components/UI/Checkbox.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
import { Switch } from "@components/UI/Switch.js";
|
import { Switch } from "@components/UI/Switch.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { toByteArray } from "base64-js";
|
import { toByteArray } from "base64-js";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { BLE } from "@components/PageComponents/Connect/BLE.js";
|
import { BLE } from "@components/PageComponents/Connect/BLE.tsx";
|
||||||
import { HTTP } from "@components/PageComponents/Connect/HTTP.js";
|
import { HTTP } from "@components/PageComponents/Connect/HTTP.tsx";
|
||||||
import { Serial } from "@components/PageComponents/Connect/Serial.js";
|
import { Serial } from "@components/PageComponents/Connect/Serial.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@components/UI/Tabs.js";
|
} from "@components/UI/Tabs.tsx";
|
||||||
import { Link } from "@components/UI/Typography/Link.js";
|
import { Link } from "@components/UI/Typography/Link.tsx";
|
||||||
import { Subtle } from "@components/UI/Typography/Subtle.js";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
|
|
||||||
export interface TabElementProps {
|
export interface TabElementProps {
|
||||||
closeDialog: () => void;
|
closeDialog: () => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
|
|
||||||
export interface PkiRegenerateDialogProps {
|
export interface PkiRegenerateDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Checkbox } from "@components/UI/Checkbox.js";
|
import { Checkbox } from "@components/UI/Checkbox.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -6,9 +6,9 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
import { Protobuf, type Types } from "@meshtastic/js";
|
import { Protobuf, type Types } from "@meshtastic/js";
|
||||||
import { fromByteArray } from "base64-js";
|
import { fromByteArray } from "base64-js";
|
||||||
import { ClipboardIcon } from "lucide-react";
|
import { ClipboardIcon } from "lucide-react";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { ClockIcon, RefreshCwIcon } from "lucide-react";
|
import { ClockIcon, RefreshCwIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "@app/core/stores/appStore";
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
|
|
||||||
export interface RemoveNodeDialogProps {
|
export interface RemoveNodeDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog.js";
|
} from "@components/UI/Dialog.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { ClockIcon, PowerIcon } from "lucide-react";
|
import { ClockIcon, PowerIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
DynamicFormField,
|
DynamicFormField,
|
||||||
type FieldProps,
|
type FieldProps,
|
||||||
} from "@components/Form/DynamicFormField.js";
|
} from "@components/Form/DynamicFormField.tsx";
|
||||||
import { FieldWrapper } from "@components/Form/FormWrapper.js";
|
import { FieldWrapper } from "@components/Form/FormWrapper.tsx";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { H4 } from "@components/UI/Typography/H4.js";
|
import { H4 } from "@components/UI/Typography/H4.tsx";
|
||||||
import { Subtle } from "@components/UI/Typography/Subtle.js";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
import {
|
import {
|
||||||
type Control,
|
type Control,
|
||||||
type DefaultValues,
|
type DefaultValues,
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
GenericInput,
|
GenericInput,
|
||||||
type InputFieldProps,
|
type InputFieldProps,
|
||||||
} from "@components/Form/FormInput.js";
|
} from "@components/Form/FormInput.tsx";
|
||||||
import {
|
import {
|
||||||
PasswordGenerator,
|
PasswordGenerator,
|
||||||
type PasswordGeneratorProps,
|
type PasswordGeneratorProps,
|
||||||
} from "@components/Form/FormPasswordGenerator.js";
|
} from "@components/Form/FormPasswordGenerator.tsx";
|
||||||
import {
|
import {
|
||||||
type SelectFieldProps,
|
type SelectFieldProps,
|
||||||
SelectInput,
|
SelectInput,
|
||||||
} from "@components/Form/FormSelect.js";
|
} from "@components/Form/FormSelect.tsx";
|
||||||
import {
|
import {
|
||||||
type ToggleFieldProps,
|
type ToggleFieldProps,
|
||||||
ToggleInput,
|
ToggleInput,
|
||||||
} from "@components/Form/FormToggle.js";
|
} from "@components/Form/FormToggle.tsx";
|
||||||
import type { Control, FieldValues } from "react-hook-form";
|
import type { Control, FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
export type FieldProps<T> =
|
export type FieldProps<T> =
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormBuilderProps,
|
BaseFormBuilderProps,
|
||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.js";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import type { ChangeEventHandler } from "react";
|
import type { ChangeEventHandler } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
import { Controller, type FieldValues } from "react-hook-form";
|
import { Controller, type FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
export interface InputFieldProps<T> extends BaseFormBuilderProps<T> {
|
export interface InputFieldProps<T> extends BaseFormBuilderProps<T> {
|
||||||
@@ -27,13 +29,28 @@ export function GenericInput<T extends FieldValues>({
|
|||||||
disabled,
|
disabled,
|
||||||
field,
|
field,
|
||||||
}: GenericFormElementProps<T, InputFieldProps<T>>) {
|
}: GenericFormElementProps<T, InputFieldProps<T>>) {
|
||||||
|
const [passwordShown, setPasswordShown] = useState(false);
|
||||||
|
const togglePasswordVisiblity = () => {
|
||||||
|
setPasswordShown(!passwordShown);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
name={field.name}
|
name={field.name}
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange, ...rest } }) => (
|
render={({ field: { value, onChange, ...rest } }) => (
|
||||||
<Input
|
<Input
|
||||||
type={field.type}
|
type={
|
||||||
|
field.type === "password" && passwordShown ? "text" : field.type
|
||||||
|
}
|
||||||
|
action={
|
||||||
|
field.type === "password"
|
||||||
|
? {
|
||||||
|
icon: passwordShown ? EyeOff : Eye,
|
||||||
|
onClick: togglePasswordVisiblity,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
step={field.properties?.step}
|
step={field.properties?.step}
|
||||||
value={field.type === "number" ? Number.parseFloat(value) : value}
|
value={field.type === "number" ? Number.parseFloat(value) : value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormBuilderProps,
|
BaseFormBuilderProps,
|
||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.js";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import { Generator } from "@components/UI/Generator.js";
|
import { Generator } from "@components/UI/Generator.tsx";
|
||||||
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import type { ChangeEventHandler, MouseEventHandler } from "react";
|
import type { ChangeEventHandler, MouseEventHandler } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
import { Controller, type FieldValues } from "react-hook-form";
|
import { Controller, type FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
|
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
|
||||||
@@ -21,13 +23,26 @@ export function PasswordGenerator<T extends FieldValues>({
|
|||||||
field,
|
field,
|
||||||
disabled,
|
disabled,
|
||||||
}: GenericFormElementProps<T, PasswordGeneratorProps<T>>) {
|
}: GenericFormElementProps<T, PasswordGeneratorProps<T>>) {
|
||||||
|
const [passwordShown, setPasswordShown] = useState(false);
|
||||||
|
const togglePasswordVisiblity = () => {
|
||||||
|
setPasswordShown(!passwordShown);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
name={field.name}
|
name={field.name}
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, ...rest } }) => (
|
render={({ field: { value, ...rest } }) => (
|
||||||
<Generator
|
<Generator
|
||||||
hide={field.hide}
|
type={field.hide && !passwordShown ? "password" : "text"}
|
||||||
|
action={
|
||||||
|
field.hide
|
||||||
|
? {
|
||||||
|
icon: passwordShown ? EyeOff : Eye,
|
||||||
|
onClick: togglePasswordVisiblity,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
devicePSKBitCount={field.devicePSKBitCount}
|
devicePSKBitCount={field.devicePSKBitCount}
|
||||||
bits={field.bits}
|
bits={field.bits}
|
||||||
inputChange={field.inputChange}
|
inputChange={field.inputChange}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormBuilderProps,
|
BaseFormBuilderProps,
|
||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.js";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@components/UI/Select.js";
|
} from "@components/UI/Select.tsx";
|
||||||
import { Controller, type FieldValues } from "react-hook-form";
|
import { Controller, type FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
export interface SelectFieldProps<T> extends BaseFormBuilderProps<T> {
|
export interface SelectFieldProps<T> extends BaseFormBuilderProps<T> {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormBuilderProps,
|
BaseFormBuilderProps,
|
||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.js";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import { Switch } from "@components/UI/Switch.js";
|
import { Switch } from "@components/UI/Switch.tsx";
|
||||||
import type { ChangeEvent } from "react";
|
import type { ChangeEvent } from "react";
|
||||||
import { Controller, type FieldValues } from "react-hook-form";
|
import { Controller, type FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
|
|
||||||
export interface FieldWrapperProps {
|
export interface FieldWrapperProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ChannelValidation } from "@app/validation/channel.js";
|
import type { ChannelValidation } from "@app/validation/channel.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useToast } from "@core/hooks/useToast.js";
|
import { useToast } from "@core/hooks/useToast.ts";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { fromByteArray, toByteArray } from "base64-js";
|
import { fromByteArray, toByteArray } from "base64-js";
|
||||||
import cryptoRandomString from "crypto-random-string";
|
import cryptoRandomString from "crypto-random-string";
|
||||||
@@ -111,10 +111,14 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
type: "select",
|
type: "select",
|
||||||
name: "role",
|
name: "role",
|
||||||
label: "Role",
|
label: "Role",
|
||||||
|
disabled: channel.index === 0,
|
||||||
description:
|
description:
|
||||||
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue: Protobuf.Channel.Channel_Role,
|
enumValue:
|
||||||
|
channel.index === 0
|
||||||
|
? { PRIMARY: 1 }
|
||||||
|
: { DISABLED: 0, SECONDARY: 2 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -127,6 +131,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
inputChange: inputChangeEvent,
|
inputChange: inputChangeEvent,
|
||||||
selectChange: selectChangeEvent,
|
selectChange: selectChangeEvent,
|
||||||
buttonClick: clickEvent,
|
buttonClick: clickEvent,
|
||||||
|
hide: true,
|
||||||
properties: {
|
properties: {
|
||||||
value: pass,
|
value: pass,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { BluetoothValidation } from "@app/validation/config/bluetooth.js";
|
import type { BluetoothValidation } from "@app/validation/config/bluetooth.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Bluetooth = (): JSX.Element => {
|
export const Bluetooth = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { DeviceValidation } from "@app/validation/config/device.js";
|
import type { DeviceValidation } from "@app/validation/config/device.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Device = (): JSX.Element => {
|
export const Device = (): JSX.Element => {
|
||||||
@@ -32,7 +32,22 @@ export const Device = (): JSX.Element => {
|
|||||||
label: "Role",
|
label: "Role",
|
||||||
description: "What role the device performs on the mesh",
|
description: "What role the device performs on the mesh",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue: Protobuf.Config.Config_DeviceConfig_Role,
|
enumValue: {
|
||||||
|
Client: Protobuf.Config.Config_DeviceConfig_Role.CLIENT,
|
||||||
|
"Client Mute":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.CLIENT_MUTE,
|
||||||
|
Router: Protobuf.Config.Config_DeviceConfig_Role.ROUTER,
|
||||||
|
Repeater: Protobuf.Config.Config_DeviceConfig_Role.REPEATER,
|
||||||
|
Tracker: Protobuf.Config.Config_DeviceConfig_Role.TRACKER,
|
||||||
|
Sensor: Protobuf.Config.Config_DeviceConfig_Role.SENSOR,
|
||||||
|
TAK: Protobuf.Config.Config_DeviceConfig_Role.TAK,
|
||||||
|
"Client Hidden":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.CLIENT_HIDDEN,
|
||||||
|
"Lost and Found":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.LOST_AND_FOUND,
|
||||||
|
"TAK Tracker":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.SENSOR,
|
||||||
|
},
|
||||||
formatEnumName: true,
|
formatEnumName: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { DisplayValidation } from "@app/validation/config/display.js";
|
import type { DisplayValidation } from "@app/validation/config/display.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Display = (): JSX.Element => {
|
export const Display = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { LoRaValidation } from "@app/validation/config/lora.js";
|
import type { LoRaValidation } from "@app/validation/config/lora.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const LoRa = (): JSX.Element => {
|
export const LoRa = (): JSX.Element => {
|
||||||
@@ -56,6 +56,13 @@ export const LoRa = (): JSX.Element => {
|
|||||||
label: "Ignore MQTT",
|
label: "Ignore MQTT",
|
||||||
description: "Don't forward MQTT messages over the mesh",
|
description: "Don't forward MQTT messages over the mesh",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "toggle",
|
||||||
|
name: "configOkToMqtt",
|
||||||
|
label: "OK to MQTT",
|
||||||
|
description:
|
||||||
|
"When set to true, this configuration indicates that the user approves the packet to be uploaded to MQTT. If set to false, remote nodes are requested not to forward packets to MQTT",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { NetworkValidation } from "@app/validation/config/network.js";
|
import type { NetworkValidation } from "@app/validation/config/network.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
|
import {
|
||||||
|
convertIntToIpAddress,
|
||||||
|
convertIpAddressToInt,
|
||||||
|
} from "@core/utils/ip.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Network = (): JSX.Element => {
|
export const Network = (): JSX.Element => {
|
||||||
@@ -13,9 +17,12 @@ export const Network = (): JSX.Element => {
|
|||||||
case: "network",
|
case: "network",
|
||||||
value: {
|
value: {
|
||||||
...data,
|
...data,
|
||||||
ipv4Config: new Protobuf.Config.Config_NetworkConfig_IpV4Config(
|
ipv4Config: new Protobuf.Config.Config_NetworkConfig_IpV4Config({
|
||||||
data.ipv4Config,
|
ip: convertIpAddressToInt(data.ipv4Config.ip) ?? 0,
|
||||||
),
|
gateway: convertIpAddressToInt(data.ipv4Config.gateway) ?? 0,
|
||||||
|
subnet: convertIpAddressToInt(data.ipv4Config.subnet) ?? 0,
|
||||||
|
dns: convertIpAddressToInt(data.ipv4Config.dns) ?? 0,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -25,7 +32,19 @@ export const Network = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<DynamicForm<NetworkValidation>
|
<DynamicForm<NetworkValidation>
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
defaultValues={config.network}
|
defaultValues={{
|
||||||
|
...config.network,
|
||||||
|
ipv4Config: {
|
||||||
|
ip: convertIntToIpAddress(config.network?.ipv4Config?.ip ?? 0),
|
||||||
|
gateway: convertIntToIpAddress(
|
||||||
|
config.network?.ipv4Config?.gateway ?? 0,
|
||||||
|
),
|
||||||
|
subnet: convertIntToIpAddress(
|
||||||
|
config.network?.ipv4Config?.subnet ?? 0,
|
||||||
|
),
|
||||||
|
dns: convertIntToIpAddress(config.network?.ipv4Config?.dns ?? 0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
fieldGroups={[
|
fieldGroups={[
|
||||||
{
|
{
|
||||||
label: "WiFi Config",
|
label: "WiFi Config",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PositionValidation } from "@app/validation/config/position.js";
|
import type { PositionValidation } from "@app/validation/config/position.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Position = (): JSX.Element => {
|
export const Position = (): JSX.Element => {
|
||||||
@@ -77,12 +77,6 @@ export const Position = (): JSX.Element => {
|
|||||||
label: "Enable Pin",
|
label: "Enable Pin",
|
||||||
description: "GPS module enable pin override",
|
description: "GPS module enable pin override",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: "number",
|
|
||||||
name: "channelPrecision",
|
|
||||||
label: "Channel Precision",
|
|
||||||
description: "GPS channel precision",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PowerValidation } from "@app/validation/config/power.js";
|
import type { PowerValidation } from "@app/validation/config/power.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Power = (): JSX.Element => {
|
export const Power = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { PkiRegenerateDialog } from "@app/components/Dialog/PkiRegenerateDialog";
|
import { PkiRegenerateDialog } from "@app/components/Dialog/PkiRegenerateDialog";
|
||||||
import { DynamicForm } from "@app/components/Form/DynamicForm.js";
|
import { DynamicForm } from "@app/components/Form/DynamicForm.tsx";
|
||||||
import {
|
import {
|
||||||
getX25519PrivateKey,
|
getX25519PrivateKey,
|
||||||
getX25519PublicKey,
|
getX25519PublicKey,
|
||||||
} from "@app/core/utils/x25519";
|
} from "@app/core/utils/x25519";
|
||||||
import type { SecurityValidation } from "@app/validation/config/security.js";
|
import type { SecurityValidation } from "@app/validation/config/security.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { fromByteArray, toByteArray } from "base64-js";
|
import { fromByteArray, toByteArray } from "base64-js";
|
||||||
import { Eye, EyeOff } from "lucide-react";
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
@@ -27,7 +27,7 @@ export const Security = (): JSX.Element => {
|
|||||||
fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
|
fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
|
||||||
);
|
);
|
||||||
const [adminKey, setAdminKey] = useState<string>(
|
const [adminKey, setAdminKey] = useState<string>(
|
||||||
fromByteArray(config.security?.adminKey ?? new Uint8Array(0)),
|
fromByteArray(config.security?.adminKey[0] ?? new Uint8Array(0)),
|
||||||
);
|
);
|
||||||
const [adminKeyValidationText, setAdminKeyValidationText] =
|
const [adminKeyValidationText, setAdminKeyValidationText] =
|
||||||
useState<string>();
|
useState<string>();
|
||||||
@@ -42,7 +42,7 @@ export const Security = (): JSX.Element => {
|
|||||||
case: "security",
|
case: "security",
|
||||||
value: {
|
value: {
|
||||||
...data,
|
...data,
|
||||||
adminKey: toByteArray(adminKey),
|
adminKey: [toByteArray(adminKey)],
|
||||||
privateKey: toByteArray(privateKey),
|
privateKey: toByteArray(privateKey),
|
||||||
publicKey: toByteArray(publicKey),
|
publicKey: toByteArray(publicKey),
|
||||||
},
|
},
|
||||||
@@ -129,8 +129,6 @@ export const Security = (): JSX.Element => {
|
|||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
|
adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
|
||||||
isManaged: config.security?.isManaged ?? false,
|
isManaged: config.security?.isManaged ?? false,
|
||||||
bluetoothLoggingEnabled:
|
|
||||||
config.security?.bluetoothLoggingEnabled ?? false,
|
|
||||||
debugLogApiEnabled: config.security?.debugLogApiEnabled ?? false,
|
debugLogApiEnabled: config.security?.debugLogApiEnabled ?? false,
|
||||||
serialEnabled: config.security?.serialEnabled ?? false,
|
serialEnabled: config.security?.serialEnabled ?? false,
|
||||||
},
|
},
|
||||||
@@ -212,18 +210,12 @@ export const Security = (): JSX.Element => {
|
|||||||
label: "Logging Settings",
|
label: "Logging Settings",
|
||||||
description: "Settings for Logging",
|
description: "Settings for Logging",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
|
||||||
type: "toggle",
|
|
||||||
name: "bluetoothLoggingEnabled",
|
|
||||||
label: "Allow Bluetooth Logging",
|
|
||||||
description:
|
|
||||||
"Enables device (serial style logs) over Bluetooth",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
name: "debugLogApiEnabled",
|
name: "debugLogApiEnabled",
|
||||||
label: "Enable Debug Log API",
|
label: "Enable Debug Log API",
|
||||||
description: "Output live debug logging over serial",
|
description:
|
||||||
|
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.js";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { subscribeAll } from "@core/subscriptions.js";
|
import { subscribeAll } from "@core/subscriptions.ts";
|
||||||
import { randId } from "@core/utils/randId.js";
|
import { randId } from "@core/utils/randId.ts";
|
||||||
import { BleConnection, Constants } from "@meshtastic/js";
|
import { BleConnection, Constants } from "@meshtastic/js";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { Label } from "@components/UI/Label.js";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
import { Switch } from "@components/UI/Switch.js";
|
import { Switch } from "@components/UI/Switch.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { subscribeAll } from "@core/subscriptions.js";
|
import { subscribeAll } from "@core/subscriptions.ts";
|
||||||
import { randId } from "@core/utils/randId.js";
|
import { randId } from "@core/utils/randId.ts";
|
||||||
import { HttpConnection } from "@meshtastic/js";
|
import { HttpConnection } from "@meshtastic/js";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.js";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { subscribeAll } from "@core/subscriptions.js";
|
import { subscribeAll } from "@core/subscriptions.ts";
|
||||||
import { randId } from "@core/utils/randId.js";
|
import { randId } from "@core/utils/randId.ts";
|
||||||
import { SerialConnection } from "@meshtastic/js";
|
import { SerialConnection } from "@meshtastic/js";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Subtle } from "@app/components/UI/Typography/Subtle.js";
|
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
|
||||||
import {
|
import {
|
||||||
type MessageWithState,
|
type MessageWithState,
|
||||||
useDevice,
|
useDevice,
|
||||||
} from "@app/core/stores/deviceStore.js";
|
} from "@app/core/stores/deviceStore.ts";
|
||||||
import { Message } from "@components/PageComponents/Messages/Message.js";
|
import { Message } from "@components/PageComponents/Messages/Message.tsx";
|
||||||
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.js";
|
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
|
||||||
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.js";
|
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.tsx";
|
||||||
import type { Protobuf, Types } from "@meshtastic/js";
|
import type { Protobuf, Types } from "@meshtastic/js";
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { MessageWithState } from "@app/core/stores/deviceStore.js";
|
import type { MessageWithState } from "@app/core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import type { Protobuf } from "@meshtastic/js";
|
import type { Protobuf } from "@meshtastic/js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { Types } from "@meshtastic/js";
|
import type { Types } from "@meshtastic/js";
|
||||||
import { SendIcon } from "lucide-react";
|
import { SendIcon } from "lucide-react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import type { Protobuf } from "@meshtastic/js";
|
import type { Protobuf } from "@meshtastic/js";
|
||||||
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
|
|
||||||
export interface TraceRouteProps {
|
export interface TraceRouteProps {
|
||||||
from?: Protobuf.Mesh.NodeInfo;
|
from?: Protobuf.Mesh.NodeInfo;
|
||||||
@@ -24,7 +25,10 @@ export const TraceRoute = ({
|
|||||||
<div className="ml-5 flex">
|
<div className="ml-5 flex">
|
||||||
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
|
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
|
||||||
{to?.user?.longName}↔
|
{to?.user?.longName}↔
|
||||||
{route.map((hop) => `${nodes.get(hop)?.user?.longName ?? "Unknown"}↔`)}
|
{route.map((hop) => {
|
||||||
|
const node = nodes.get(hop);
|
||||||
|
return `${node?.user?.longName ?? (node?.num ? numberToHexUnpadded(node.num) : "Unknown")}↔`;
|
||||||
|
})}
|
||||||
{from?.user?.longName}
|
{from?.user?.longName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.js";
|
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const AmbientLighting = (): JSX.Element => {
|
export const AmbientLighting = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AudioValidation } from "@app/validation/moduleConfig/audio.js";
|
import type { AudioValidation } from "@app/validation/moduleConfig/audio.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Audio = (): JSX.Element => {
|
export const Audio = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.js";
|
import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const CannedMessage = (): JSX.Element => {
|
export const CannedMessage = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.js";
|
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const DetectionSensor = (): JSX.Element => {
|
export const DetectionSensor = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.js";
|
import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const ExternalNotification = (): JSX.Element => {
|
export const ExternalNotification = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.js";
|
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const MQTT = (): JSX.Element => {
|
export const MQTT = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.js";
|
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const NeighborInfo = (): JSX.Element => {
|
export const NeighborInfo = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.js";
|
import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Paxcounter = (): JSX.Element => {
|
export const Paxcounter = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.js";
|
import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const RangeTest = (): JSX.Element => {
|
export const RangeTest = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { SerialValidation } from "@app/validation/moduleConfig/serial.js";
|
import type { SerialValidation } from "@app/validation/moduleConfig/serial.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Serial = (): JSX.Element => {
|
export const Serial = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.js";
|
import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.ts";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const StoreForward = (): JSX.Element => {
|
export const StoreForward = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.js";
|
import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.tsx";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Telemetry = (): JSX.Element => {
|
export const Telemetry = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn.js";
|
import { cn } from "@app/core/utils/cn.ts";
|
||||||
import { AlignLeftIcon, type LucideIcon } from "lucide-react";
|
import { AlignLeftIcon, type LucideIcon } from "lucide-react";
|
||||||
import Footer from "./UI/Footer";
|
import Footer from "./UI/Footer";
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ export interface PageLayoutProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
actions?: {
|
actions?: {
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
|
iconClasses?: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,7 @@ export const PageLayout = ({
|
|||||||
className="transition-all hover:text-accent"
|
className="transition-all hover:text-accent"
|
||||||
onClick={action.onClick}
|
onClick={action.onClick}
|
||||||
>
|
>
|
||||||
<action.icon />
|
<action.icon className={action.iconClasses} />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
|
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
|
||||||
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
|
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
|
||||||
import { Subtle } from "@components/UI/Typography/Subtle.js";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { Page } from "@core/stores/deviceStore.js";
|
import type { Page } from "@core/stores/deviceStore.ts";
|
||||||
import {
|
import {
|
||||||
BatteryMediumIcon,
|
BatteryMediumIcon,
|
||||||
CpuIcon,
|
CpuIcon,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useToast } from "@core/hooks/useToast.js";
|
import { useToast } from "@core/hooks/useToast.ts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Toast,
|
Toast,
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
ToastProvider,
|
ToastProvider,
|
||||||
ToastTitle,
|
ToastTitle,
|
||||||
ToastViewport,
|
ToastViewport,
|
||||||
} from "@components/UI/Toast.js";
|
} from "@components/UI/Toast.tsx";
|
||||||
|
|
||||||
export function Toaster() {
|
export function Toaster() {
|
||||||
const { toasts } = useToast();
|
const { toasts } = useToast();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|||||||
import { Check } from "lucide-react";
|
import { Check } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Checkbox = React.forwardRef<
|
const Checkbox = React.forwardRef<
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Command as CommandPrimitive } from "cmdk";
|
|||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { Dialog, DialogContent } from "@components/UI/Dialog.js";
|
import { Dialog, DialogContent } from "@components/UI/Dialog.tsx";
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Command = React.forwardRef<
|
const Command = React.forwardRef<
|
||||||
React.ElementRef<typeof CommandPrimitive>,
|
React.ElementRef<typeof CommandPrimitive>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root;
|
const Dialog = DialogPrimitive.Root;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|||||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Input } from "@components/UI/Input.js";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@components/UI/Select.js";
|
} from "@components/UI/Select.tsx";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
|
export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
|
||||||
hide?: boolean;
|
type: "text" | "password";
|
||||||
devicePSKBitCount?: number;
|
devicePSKBitCount?: number;
|
||||||
value: string;
|
value: string;
|
||||||
variant: "default" | "invalid";
|
variant: "default" | "invalid";
|
||||||
@@ -31,7 +31,7 @@ export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
|
|||||||
const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
hide = true,
|
type,
|
||||||
devicePSKBitCount,
|
devicePSKBitCount,
|
||||||
variant,
|
variant,
|
||||||
value,
|
value,
|
||||||
@@ -68,7 +68,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
type={hide ? "password" : "text"}
|
type={type}
|
||||||
id="pskInput"
|
id="pskInput"
|
||||||
variant={variant}
|
variant={variant}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Label = React.forwardRef<
|
const Label = React.forwardRef<
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as MenubarPrimitive from "@radix-ui/react-menubar";
|
|||||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const MenubarMenu = MenubarPrimitive.Menu;
|
const MenubarMenu = MenubarPrimitive.Menu;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Popover = PopoverPrimitive.Root;
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const ScrollArea = React.forwardRef<
|
const ScrollArea = React.forwardRef<
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as SelectPrimitive from "@radix-ui/react-select";
|
|||||||
import { Check, ChevronDown } from "lucide-react";
|
import { Check, ChevronDown } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Select = SelectPrimitive.Root;
|
const Select = SelectPrimitive.Root;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Separator = React.forwardRef<
|
const Separator = React.forwardRef<
|
||||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { H4 } from "@components/UI/Typography/H4.js";
|
import { H4 } from "@components/UI/Typography/H4.tsx";
|
||||||
|
|
||||||
export interface SidebarSectionProps {
|
export interface SidebarSectionProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
export interface SidebarButtonProps {
|
export interface SidebarButtonProps {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Switch = React.forwardRef<
|
const Switch = React.forwardRef<
|
||||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const Tabs = TabsPrimitive.Root;
|
const Tabs = TabsPrimitive.Root;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority";
|
|||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider;
|
const ToastProvider = ToastPrimitives.Provider;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.js";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
const TooltipProvider = TooltipPrimitive.Provider;
|
const TooltipProvider = TooltipPrimitive.Provider;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn.js";
|
import { cn } from "@app/core/utils/cn.ts";
|
||||||
|
|
||||||
export interface H4Props {
|
export interface H4Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn.js";
|
import { cn } from "@app/core/utils/cn.ts";
|
||||||
|
|
||||||
export interface SubtleProps {
|
export interface SubtleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
export interface ThemeControllerProps {
|
export interface ThemeControllerProps {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type ReactNode, useSyncExternalStore } from "react";
|
import { type ReactNode, useSyncExternalStore } from "react";
|
||||||
|
|
||||||
import type { ToastActionElement, ToastProps } from "@components/UI/Toast.js";
|
import type { ToastActionElement, ToastProps } from "@components/UI/Toast.tsx";
|
||||||
|
|
||||||
const TOAST_LIMIT = 1;
|
const TOAST_LIMIT = 1;
|
||||||
const TOAST_REMOVE_DELAY = 1000000;
|
const TOAST_REMOVE_DELAY = 1000000;
|
||||||
@@ -80,7 +80,7 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
...t,
|
...t,
|
||||||
open: false,
|
open: false,
|
||||||
}
|
}
|
||||||
: t,
|
: t
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -190,4 +190,4 @@ function useToast() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useToast, toast };
|
export { toast, useToast };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Device } from "@core/stores/deviceStore.js";
|
import type { Device } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf, type Types } from "@meshtastic/js";
|
import { Protobuf, type Types } from "@meshtastic/js";
|
||||||
|
|
||||||
export const subscribeAll = (
|
export const subscribeAll = (
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export function convertIntToIpAddress(int: number): string {
|
||||||
|
return `${int & 0xff}.${(int >> 8) & 0xff}.${(int >> 16) & 0xff}.${(int >> 24) & 0xff}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertIpAddressToInt(ip: string): number | null {
|
||||||
|
return (
|
||||||
|
ip
|
||||||
|
.split(".")
|
||||||
|
.reverse()
|
||||||
|
.reduce((ipnum, octet) => {
|
||||||
|
return (ipnum << 8) + Number.parseInt(octet);
|
||||||
|
}, 0) >>> 0
|
||||||
|
);
|
||||||
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import "maplibre-gl/dist/maplibre-gl.css";
|
|||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import { App } from "@app/App.js";
|
import { App } from "@app/App.tsx";
|
||||||
|
|
||||||
const container = document.getElementById("root") as HTMLElement;
|
const container = document.getElementById("root") as HTMLElement;
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import {
|
|||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@app/components/UI/Tabs.js";
|
} from "@app/components/UI/Tabs.tsx";
|
||||||
import { Channel } from "@components/PageComponents/Channel.js";
|
import { Channel } from "@components/PageComponents/Channel.tsx";
|
||||||
import { PageLayout } from "@components/PageLayout.js";
|
import { PageLayout } from "@components/PageLayout.tsx";
|
||||||
import { Sidebar } from "@components/Sidebar.js";
|
import { Sidebar } from "@components/Sidebar.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Types } from "@meshtastic/js";
|
import { Types } from "@meshtastic/js";
|
||||||
import type { Protobuf } from "@meshtastic/js";
|
import type { Protobuf } from "@meshtastic/js";
|
||||||
import { ImportIcon, QrCodeIcon } from "lucide-react";
|
import { ImportIcon, QrCodeIcon } from "lucide-react";
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Bluetooth } from "@components/PageComponents/Config/Bluetooth.js";
|
import { Bluetooth } from "@components/PageComponents/Config/Bluetooth.tsx";
|
||||||
import { Device } from "@components/PageComponents/Config/Device.js";
|
import { Device } from "@components/PageComponents/Config/Device.tsx";
|
||||||
import { Display } from "@components/PageComponents/Config/Display.js";
|
import { Display } from "@components/PageComponents/Config/Display.tsx";
|
||||||
import { LoRa } from "@components/PageComponents/Config/LoRa.js";
|
import { LoRa } from "@components/PageComponents/Config/LoRa.tsx";
|
||||||
import { Network } from "@components/PageComponents/Config/Network.js";
|
import { Network } from "@components/PageComponents/Config/Network.tsx";
|
||||||
import { Position } from "@components/PageComponents/Config/Position.js";
|
import { Position } from "@components/PageComponents/Config/Position.tsx";
|
||||||
import { Power } from "@components/PageComponents/Config/Power.js";
|
import { Power } from "@components/PageComponents/Config/Power.tsx";
|
||||||
import { Security } from "@components/PageComponents/Config/Security.js";
|
import { Security } from "@components/PageComponents/Config/Security.tsx";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@components/UI/Tabs.js";
|
} from "@components/UI/Tabs.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
|
|
||||||
export const DeviceConfig = (): JSX.Element => {
|
export const DeviceConfig = (): JSX.Element => {
|
||||||
const { metadata } = useDevice();
|
const { metadata } = useDevice();
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { AmbientLighting } from "@app/components/PageComponents/ModuleConfig/AmbientLighting.js";
|
import { AmbientLighting } from "@app/components/PageComponents/ModuleConfig/AmbientLighting.tsx";
|
||||||
import { DetectionSensor } from "@app/components/PageComponents/ModuleConfig/DetectionSensor.js";
|
import { DetectionSensor } from "@app/components/PageComponents/ModuleConfig/DetectionSensor.tsx";
|
||||||
import { NeighborInfo } from "@app/components/PageComponents/ModuleConfig/NeighborInfo.js";
|
import { NeighborInfo } from "@app/components/PageComponents/ModuleConfig/NeighborInfo.tsx";
|
||||||
import { Audio } from "@components/PageComponents/ModuleConfig/Audio.js";
|
import { Audio } from "@components/PageComponents/ModuleConfig/Audio.tsx";
|
||||||
import { CannedMessage } from "@components/PageComponents/ModuleConfig/CannedMessage.js";
|
import { CannedMessage } from "@components/PageComponents/ModuleConfig/CannedMessage.tsx";
|
||||||
import { ExternalNotification } from "@components/PageComponents/ModuleConfig/ExternalNotification.js";
|
import { ExternalNotification } from "@components/PageComponents/ModuleConfig/ExternalNotification.tsx";
|
||||||
import { MQTT } from "@components/PageComponents/ModuleConfig/MQTT.js";
|
import { MQTT } from "@components/PageComponents/ModuleConfig/MQTT.tsx";
|
||||||
import { Paxcounter } from "@components/PageComponents/ModuleConfig/Paxcounter.js";
|
import { Paxcounter } from "@components/PageComponents/ModuleConfig/Paxcounter.tsx";
|
||||||
import { RangeTest } from "@components/PageComponents/ModuleConfig/RangeTest.js";
|
import { RangeTest } from "@components/PageComponents/ModuleConfig/RangeTest.tsx";
|
||||||
import { Serial } from "@components/PageComponents/ModuleConfig/Serial.js";
|
import { Serial } from "@components/PageComponents/ModuleConfig/Serial.tsx";
|
||||||
import { StoreForward } from "@components/PageComponents/ModuleConfig/StoreForward.js";
|
import { StoreForward } from "@components/PageComponents/ModuleConfig/StoreForward.tsx";
|
||||||
import { Telemetry } from "@components/PageComponents/ModuleConfig/Telemetry.js";
|
import { Telemetry } from "@components/PageComponents/ModuleConfig/Telemetry.tsx";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@components/UI/Tabs.js";
|
} from "@components/UI/Tabs.tsx";
|
||||||
|
|
||||||
export const ModuleConfig = (): JSX.Element => {
|
export const ModuleConfig = (): JSX.Element => {
|
||||||
const tabs = [
|
const tabs = [
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
||||||
import { PageLayout } from "@components/PageLayout.js";
|
import { PageLayout } from "@components/PageLayout.tsx";
|
||||||
import { Sidebar } from "@components/Sidebar.js";
|
import { Sidebar } from "@components/Sidebar.tsx";
|
||||||
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
|
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
|
||||||
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
|
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
|
||||||
import { useToast } from "@core/hooks/useToast.js";
|
import { useToast } from "@core/hooks/useToast.ts";
|
||||||
import { DeviceConfig } from "@pages/Config/DeviceConfig.js";
|
import { DeviceConfig } from "@pages/Config/DeviceConfig.tsx";
|
||||||
import { ModuleConfig } from "@pages/Config/ModuleConfig.js";
|
import { ModuleConfig } from "@pages/Config/ModuleConfig.tsx";
|
||||||
import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react";
|
import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useAppStore } from "@app/core/stores/appStore.js";
|
import { useAppStore } from "@app/core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
|
import { useDeviceStore } from "@app/core/stores/deviceStore.ts";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Separator } from "@components/UI/Seperator.js";
|
import { Separator } from "@components/UI/Seperator.tsx";
|
||||||
import { H3 } from "@components/UI/Typography/H3.js";
|
import { H3 } from "@components/UI/Typography/H3.tsx";
|
||||||
import { Subtle } from "@components/UI/Typography/Subtle.js";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
import {
|
import {
|
||||||
BluetoothIcon,
|
BluetoothIcon,
|
||||||
ListPlusIcon,
|
ListPlusIcon,
|
||||||
|
|||||||
+11
-9
@@ -1,12 +1,13 @@
|
|||||||
import { Subtle } from "@app/components/UI/Typography/Subtle.js";
|
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
|
||||||
import { cn } from "@app/core/utils/cn.js";
|
import { cn } from "@app/core/utils/cn.ts";
|
||||||
import { PageLayout } from "@components/PageLayout.js";
|
import { PageLayout } from "@components/PageLayout.tsx";
|
||||||
import { Sidebar } from "@components/Sidebar.js";
|
import { Sidebar } from "@components/Sidebar.tsx";
|
||||||
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
|
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
|
||||||
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
|
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { bbox, lineString } from "@turf/turf";
|
import { bbox, lineString } from "@turf/turf";
|
||||||
import {
|
import {
|
||||||
BoxSelectIcon,
|
BoxSelectIcon,
|
||||||
@@ -180,7 +181,8 @@ export const MapPage = (): JSX.Element => {
|
|||||||
<div className="flex cursor-pointer gap-2 rounded-md border bg-backgroundPrimary p-1.5">
|
<div className="flex cursor-pointer gap-2 rounded-md border bg-backgroundPrimary p-1.5">
|
||||||
<Hashicon value={node.num.toString()} size={22} />
|
<Hashicon value={node.num.toString()} size={22} />
|
||||||
<Subtle className={cn(zoom < 12 && "hidden")}>
|
<Subtle className={cn(zoom < 12 && "hidden")}>
|
||||||
{node.user?.longName}
|
{node.user?.longName ||
|
||||||
|
`!${numberToHexUnpadded(node.num)}`}
|
||||||
</Subtle>
|
</Subtle>
|
||||||
</div>
|
</div>
|
||||||
</Marker>
|
</Marker>
|
||||||
|
|||||||
+31
-11
@@ -1,14 +1,15 @@
|
|||||||
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.js";
|
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.tsx";
|
||||||
import { PageLayout } from "@components/PageLayout.js";
|
import { PageLayout } from "@components/PageLayout.tsx";
|
||||||
import { Sidebar } from "@components/Sidebar.js";
|
import { Sidebar } from "@components/Sidebar.tsx";
|
||||||
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
|
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
|
||||||
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
|
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
|
||||||
import { useToast } from "@core/hooks/useToast.js";
|
import { useToast } from "@core/hooks/useToast.ts";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf, Types } from "@meshtastic/js";
|
import { Protobuf, Types } from "@meshtastic/js";
|
||||||
import { getChannelName } from "@pages/Channels.js";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { HashIcon, WaypointsIcon } from "lucide-react";
|
import { getChannelName } from "@pages/Channels.tsx";
|
||||||
|
import { HashIcon, LockIcon, LockOpenIcon, WaypointsIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const MessagesPage = (): JSX.Element => {
|
export const MessagesPage = (): JSX.Element => {
|
||||||
@@ -28,6 +29,8 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
);
|
);
|
||||||
const currentChannel = channels.get(activeChat);
|
const currentChannel = channels.get(activeChat);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const node = nodes.get(activeChat);
|
||||||
|
const nodeHex = node?.num ? numberToHexUnpadded(node.num) : "Unknown";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -56,7 +59,7 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
{filteredNodes.map((node) => (
|
{filteredNodes.map((node) => (
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
key={node.num}
|
key={node.num}
|
||||||
label={node.user?.longName ?? "Unknown"}
|
label={node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`}
|
||||||
active={activeChat === node.num}
|
active={activeChat === node.num}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setChatType("direct");
|
setChatType("direct");
|
||||||
@@ -73,12 +76,29 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
chatType === "broadcast" && currentChannel
|
chatType === "broadcast" && currentChannel
|
||||||
? getChannelName(currentChannel)
|
? getChannelName(currentChannel)
|
||||||
: chatType === "direct" && nodes.get(activeChat)
|
: chatType === "direct" && nodes.get(activeChat)
|
||||||
? nodes.get(activeChat)?.user?.longName ?? "Unknown"
|
? nodes.get(activeChat)?.user?.longName ?? nodeHex
|
||||||
: "Loading..."
|
: "Loading..."
|
||||||
}`}
|
}`}
|
||||||
actions={
|
actions={
|
||||||
chatType === "direct"
|
chatType === "direct"
|
||||||
? [
|
? [
|
||||||
|
{
|
||||||
|
icon: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? LockIcon
|
||||||
|
: LockOpenIcon,
|
||||||
|
iconClasses: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? "text-green-600"
|
||||||
|
: "text-yellow-300",
|
||||||
|
async onClick() {
|
||||||
|
const targetNode = nodes.get(activeChat)?.num;
|
||||||
|
if (targetNode === undefined) return;
|
||||||
|
toast({
|
||||||
|
title: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? "Chat is using PKI encryption."
|
||||||
|
: "Chat is using PSK encryption.",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: WaypointsIcon,
|
icon: WaypointsIcon,
|
||||||
async onClick() {
|
async onClick() {
|
||||||
|
|||||||
+17
-8
@@ -1,14 +1,15 @@
|
|||||||
import Footer from "@app/components/UI/Footer";
|
import Footer from "@app/components/UI/Footer";
|
||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "@app/core/stores/appStore";
|
||||||
import { Sidebar } from "@components/Sidebar.js";
|
import { Sidebar } from "@components/Sidebar.tsx";
|
||||||
import { Button } from "@components/UI/Button.js";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.js";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { Table } from "@components/generic/Table/index.js";
|
import { Table } from "@components/generic/Table/index.tsx";
|
||||||
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js";
|
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
|
import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { base16 } from "rfc4648";
|
import { base16 } from "rfc4648";
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
{ title: "MAC Address", type: "normal", sortable: true },
|
{ title: "MAC Address", type: "normal", sortable: true },
|
||||||
{ title: "Last Heard", type: "normal", sortable: true },
|
{ title: "Last Heard", type: "normal", sortable: true },
|
||||||
{ title: "SNR", type: "normal", sortable: true },
|
{ title: "SNR", type: "normal", sortable: true },
|
||||||
|
{ title: "Encryption", type: "normal", sortable: false },
|
||||||
{ title: "Connection", type: "normal", sortable: true },
|
{ title: "Connection", type: "normal", sortable: true },
|
||||||
{ title: "Remove", type: "normal", sortable: false },
|
{ title: "Remove", type: "normal", sortable: false },
|
||||||
]}
|
]}
|
||||||
@@ -49,7 +51,7 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
? `Meshtastic ${base16
|
? `Meshtastic ${base16
|
||||||
.stringify(node.user?.macaddr.subarray(4, 6) ?? [])
|
.stringify(node.user?.macaddr.subarray(4, 6) ?? [])
|
||||||
.toLowerCase()}`
|
.toLowerCase()}`
|
||||||
: `UNK: ${node.num}`)}
|
: `!${numberToHexUnpadded(node.num)}`)}
|
||||||
</h1>,
|
</h1>,
|
||||||
|
|
||||||
<Mono key="model">
|
<Mono key="model">
|
||||||
@@ -73,6 +75,13 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
|
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
|
||||||
{(node.snr + 10) * 5}raw
|
{(node.snr + 10) * 5}raw
|
||||||
</Mono>,
|
</Mono>,
|
||||||
|
<Mono key="pki">
|
||||||
|
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
|
||||||
|
<LockIcon className="text-green-600" />
|
||||||
|
) : (
|
||||||
|
<LockOpenIcon className="text-yellow-300" />
|
||||||
|
)}
|
||||||
|
</Mono>,
|
||||||
<Mono key="hops">
|
<Mono key="hops">
|
||||||
{node.lastHeard !== 0
|
{node.lastHeard !== 0
|
||||||
? node.viaMqtt === false && node.hopsAway === 0
|
? node.viaMqtt === false && node.hopsAway === 0
|
||||||
|
|||||||
@@ -60,4 +60,7 @@ export class LoRaValidation
|
|||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
ignoreMqtt: boolean;
|
ignoreMqtt: boolean;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
configOkToMqtt: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,21 +41,24 @@ export class NetworkValidation
|
|||||||
|
|
||||||
export class NetworkValidationIpV4Config
|
export class NetworkValidationIpV4Config
|
||||||
implements
|
implements
|
||||||
Omit<Protobuf.Config.Config_NetworkConfig_IpV4Config, keyof Message>
|
Omit<
|
||||||
|
Protobuf.Config.Config_NetworkConfig_IpV4Config,
|
||||||
|
keyof Message | "ip" | "gateway" | "subnet" | "dns"
|
||||||
|
>
|
||||||
{
|
{
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
ip: number;
|
ip: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
gateway: number;
|
gateway: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
subnet: number;
|
subnet: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
dns: number;
|
dns: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,4 @@ export class PositionValidation
|
|||||||
|
|
||||||
@IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode)
|
@IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode)
|
||||||
gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode;
|
gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode;
|
||||||
|
|
||||||
@IsArray()
|
|
||||||
channelPrecision: number[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { IsArray, IsBoolean, IsNumber, IsString, IsUrl } from "class-validator";
|
import { IsArray, IsBoolean, IsNumber, IsString, IsUrl } from "class-validator";
|
||||||
|
|
||||||
import type { RasterSource } from "@core/stores/appStore.js";
|
import type { RasterSource } from "@core/stores/appStore.ts";
|
||||||
|
|
||||||
export class MapValidation {
|
export class MapValidation {
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
|||||||
+2
-1
@@ -34,6 +34,7 @@
|
|||||||
"@types/w3c-web-serial"
|
"@types/w3c-web-serial"
|
||||||
],
|
],
|
||||||
"strictPropertyInitialization": false,
|
"strictPropertyInitialization": false,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -1,11 +1,10 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
import { visualizer } from "rollup-plugin-visualizer";
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import EnvironmentPlugin from "vite-plugin-environment";
|
import EnvironmentPlugin from "vite-plugin-environment";
|
||||||
|
|
||||||
import react from "@vitejs/plugin-react";
|
|
||||||
|
|
||||||
let hash = "";
|
let hash = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user