51 Commits
Author SHA1 Message Date
rcarterazandGitHub 72fc3ea337 Merge pull request #336 from rcarteraz/master
Fix Title
2024-11-09 10:43:00 -07:00
rcarterazandGitHub 82f4784107 Merge pull request #2 from rcarteraz/fix-title
Fix Title
2024-11-09 10:38:35 -07:00
rcarteraz 4f9fb9976d fix title 2024-11-09 10:37:06 -07:00
Sacha Weatherstone 42068ad3d8 move to RsBuild 2024-10-06 21:24:12 +10:00
Hunter ThornsberryandGitHub 62f8c4509e Merge pull request #311 from medentem/master
Added Password Visibility Toggle
2024-10-02 23:07:11 -04:00
Hunter Thornsberry d699764546 biome 2024-10-02 22:07:45 -04:00
medentem 8549d56c21 biome 2024-10-02 19:33:09 -05:00
medentem 4b532fc7f8 added password visibility toggle 2024-10-02 15:54:53 -05:00
Hunter ThornsberryandGitHub 06d2c393ce Merge pull request #310 from Hunter275/remove-client-router
Remove router-client
2024-09-22 19:54:57 -04:00
Hunter Thornsberry cecdf9758b remove router-client 2024-09-22 18:25:35 -04:00
Hunter ThornsberryandGitHub 02cb4f2584 Merge pull request #303 from Hunter275/gps-precision 2024-09-18 13:45:17 -04:00
Hunter ThornsberryandGitHub 8cfcd7b1af Merge pull request #306 from KomelT/feature/better-unknown
Unknown nodes
2024-09-17 16:26:56 -04:00
Tilen KomelandGitHub c0cb059f52 Merge branch 'meshtastic:master' into feature/better-unknown 2024-09-17 21:10:29 +02:00
Tilen Komel a2a9b37238 Unknown to node hex on other missing places 2024-09-17 21:08:35 +02:00
Hunter ThornsberryandGitHub 57d0d27bbb Merge pull request #304 from KomelT/feature/better-unknown
Unknown nodes
2024-09-17 14:54:05 -04:00
Hunter Thornsberry 0e92dd9bea There is no longer a setting here 2024-09-17 13:53:36 -04:00
Tilen Komel c16ebf3917 Show hex on map instead of empty 2024-09-17 19:50:27 +02:00
Hunter Thornsberry 3d3a08a23f replace with select 2024-09-17 13:47:07 -04:00
Hunter ThornsberryandGitHub 4d1227a942 Merge pull request #269 from KomelT/fix/static-ip-display
Fix/static ip display
2024-09-17 13:27:12 -04:00
Hunter Thornsberry a8ee273b24 biome 2024-09-17 12:36:49 -04:00
Hunter Thornsberry 3ee7a57480 rewrite convertIpAddressToInt 2024-09-17 12:35:51 -04:00
Tilen Komel 2f2c777c56 Optimize 2024-09-17 07:32:12 +02:00
Tilen KomelandGitHub 2f36118e9d Merge branch 'meshtastic:master' into fix/static-ip-display 2024-09-17 07:16:54 +02:00
Hunter ThornsberryandGitHub a6d161581f Merge pull request #300 from Hunter275/primary-channel
Only allow channel index 0 to be PRIMARY
2024-09-15 19:55:24 -04:00
Hunter Thornsberry d05ea5a2cc Merge remote-tracking branch 'meshtastic-remote/master' into fix/static-ip-display 2024-09-15 19:39:13 -04:00
Hunter Thornsberry 471db94242 Merge branch 'master' into fix/static-ip-display 2024-09-15 19:38:03 -04:00
Hunter Thornsberry 2654e4fbc9 biome manual fixes 2024-09-15 19:24:16 -04:00
Hunter Thornsberry f2aa5bfbee biome 2024-09-15 19:23:44 -04:00
Hunter Thornsberry 3b018b0c70 Only allow channel index 0 to be PRIMARY 2024-09-15 19:23:07 -04:00
Hunter ThornsberryandGitHub 921db10d91 Merge pull request #297 from Hunter275/js-version-bump
js version bump
2024-09-12 17:20:09 -04:00
Hunter Thornsberry bf4f593e3a use new js and remove polyfills 2024-09-12 14:20:32 -04:00
Hunter Thornsberry 1e061a1e19 polyfill 2024-09-11 23:07:52 -04:00
Hunter Thornsberry 9b9f537e2c js version bump 2024-09-11 22:13:46 -04:00
Hunter ThornsberryandGitHub 985cce0b0d Merge pull request #294 from meshtastic/pki
PKI
2024-09-11 17:51:31 -04:00
Hunter ThornsberryandGitHub 3fe38eb506 Merge pull request #295 from meshtastic/master
Update release.yml
2024-09-10 20:53:31 -04:00
Hunter ThornsberryandGitHub 51081d3052 Update release.yml
Create a build artifact on release
2024-09-10 20:51:50 -04:00
Hunter Thornsberry c08f6d16bb Merge branch 'master' into pki 2024-09-09 18:55:02 -04:00
Hunter ThornsberryandGitHub 62ad4c49f8 Merge pull request #293 from Hunter275/pki-nodelist
Node List & DMs
2024-09-09 18:50:29 -04:00
Hunter Thornsberry 3b0a1e6108 biome 2024-09-09 18:37:22 -04:00
Hunter Thornsberry c2f2205626 cleanup 2024-09-09 18:25:58 -04:00
Hunter ThornsberryandGitHub 87c729d694 Merge branch 'pki' into pki-nodelist 2024-09-09 18:01:17 -04:00
Hunter Thornsberry 8e4f60edf3 biome 2024-09-09 16:36:56 -04:00
Hunter Thornsberry 8811eee9f5 Remove bluetooth debugging and reword generic debug 2024-09-09 16:36:17 -04:00
Hunter Thornsberry 2af93f1acd Fix protobufs, add configOkToMqtt, add PKI icons 2024-09-09 16:22:48 -04:00
Hunter Thornsberry 78a35544c7 Node list and DMs now show icons 2024-09-08 19:48:42 -04:00
Hunter Thornsberry 3ad2d650b0 update protobufs 2024-09-08 18:59:57 -04:00
Hunter ThornsberryandGitHub bf425a8ec7 Merge pull request #291 from Kongduino/patch-1 2024-09-07 12:21:47 -04:00
KongduinoandGitHub a7d0d36086 Update index.tsx
"at least", two words. Thanks...
2024-09-07 13:32:16 +08:00
Tilen Komel 8ed3ce8203 Error & Format fixing 2024-08-21 23:13:35 +02:00
Tilen Komel ebd5a3d3a6 Implemented IP utils 2024-08-21 21:53:07 +02:00
Tilen Komel 1cdf18747d Added ip utils 2024-08-21 21:53:07 +02:00
97 changed files with 1488 additions and 816 deletions
+6
View File
@@ -27,6 +27,12 @@ jobs:
- name: Package Output
run: pnpm package
- name: Archive compressed build
uses: actions/upload-artifact@v4
with:
name: build
path: dist/build.tar
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
+11 -10
View File
@@ -5,11 +5,12 @@
"description": "Meshtastic web client",
"license": "GPL-3.0-only",
"scripts": {
"dev": "vite --host",
"build": "tsc && pnpm check && vite build ",
"build": "rsbuild build",
"check": "biome check .",
"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/)"
},
"repository": {
@@ -23,7 +24,7 @@
"dependencies": {
"@bufbuild/protobuf": "^1.10.0",
"@emeraldpay/hashicon-react": "^0.5.2",
"@meshtastic/js": "2.3.7-1",
"@meshtastic/js": "2.3.7-5",
"@noble/curves": "^1.5.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.0",
@@ -49,7 +50,7 @@
"crypto-random-string": "^5.0.0",
"immer": "^10.1.1",
"lucide-react": "^0.363.0",
"mapbox-gl": "npm:empty-npm-package@^1.0.0",
"mapbox-gl": "^3.6.0",
"maplibre-gl": "4.1.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -60,18 +61,20 @@
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"timeago-react": "^3.0.6",
"vite-plugin-node-polyfills": "^0.22.0",
"zustand": "4.5.2"
},
"devDependencies": {
"@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/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/w3c-web-serial": "^1.0.6",
"@types/web-bluetooth": "^0.0.20",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"gzipper": "^7.2.0",
"postcss": "^8.4.38",
@@ -79,8 +82,6 @@
"tailwindcss": "^3.4.4",
"tar": "^6.2.1",
"tslib": "^2.6.3",
"typescript": "^5.5.2",
"vite": "^5.3.1",
"vite-plugin-environment": "^1.1.3"
"typescript": "^5.5.2"
}
}
+971 -453
View File
File diff suppressed because it is too large Load Diff
+30
View File
@@ -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
View File
@@ -1,15 +1,15 @@
import { DeviceWrapper } from "@app/DeviceWrapper.js";
import { PageRouter } from "@app/PageRouter.js";
import { CommandPalette } from "@components/CommandPalette.js";
import { DeviceSelector } from "@components/DeviceSelector.js";
import { DialogManager } from "@components/Dialog/DialogManager.js";
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.js";
import { Toaster } from "@components/Toaster.js";
import Footer from "@components/UI/Footer.js";
import { ThemeController } from "@components/generic/ThemeController.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { Dashboard } from "@pages/Dashboard/index.js";
import { DeviceWrapper } from "@app/DeviceWrapper.tsx";
import { PageRouter } from "@app/PageRouter.tsx";
import { CommandPalette } from "@components/CommandPalette.tsx";
import { DeviceSelector } from "@components/DeviceSelector.tsx";
import { DialogManager } from "@components/Dialog/DialogManager.tsx";
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.tsx";
import { Toaster } from "@components/Toaster.tsx";
import Footer from "@components/UI/Footer.tsx";
import { ThemeController } from "@components/generic/ThemeController.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { Dashboard } from "@pages/Dashboard/index.tsx";
import { MapProvider } from "react-map-gl";
export const App = (): JSX.Element => {
+2 -2
View File
@@ -1,5 +1,5 @@
import { DeviceContext } from "@core/stores/deviceStore.js";
import type { Device } from "@core/stores/deviceStore.js";
import { DeviceContext } from "@core/stores/deviceStore.ts";
import type { Device } from "@core/stores/deviceStore.ts";
import type { ReactNode } from "react";
export interface DeviceWrapperProps {
+6 -6
View File
@@ -1,9 +1,9 @@
import { useDevice } from "@core/stores/deviceStore.js";
import { ChannelsPage } from "@pages/Channels.js";
import { ConfigPage } from "@pages/Config/index.js";
import { MapPage } from "@pages/Map.js";
import { MessagesPage } from "@pages/Messages.js";
import { NodesPage } from "@pages/Nodes.js";
import { useDevice } from "@core/stores/deviceStore.ts";
import { ChannelsPage } from "@pages/Channels.tsx";
import { ConfigPage } from "@pages/Config/index.tsx";
import { MapPage } from "@pages/Map.tsx";
import { MessagesPage } from "@pages/Messages.tsx";
import { NodesPage } from "@pages/Nodes.tsx";
export const PageRouter = (): JSX.Element => {
const { activePage } = useDevice();
+3 -3
View File
@@ -5,9 +5,9 @@ import {
CommandInput,
CommandItem,
CommandList,
} from "@components/UI/Command.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.js";
} from "@components/UI/Command.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { useCommandState } from "cmdk";
import {
+5 -5
View File
@@ -1,8 +1,8 @@
import { DeviceSelectorButton } from "@components/DeviceSelectorButton.js";
import { Separator } from "@components/UI/Seperator.js";
import { Code } from "@components/UI/Typography/Code.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { DeviceSelectorButton } from "@components/DeviceSelectorButton.tsx";
import { Separator } from "@components/UI/Seperator.tsx";
import { Code } from "@components/UI/Typography/Code.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import {
HomeIcon,
+5 -5
View File
@@ -1,5 +1,5 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import { Button } from "@components/UI/Button.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import { Button } from "@components/UI/Button.tsx";
import {
Dialog,
DialogContent,
@@ -7,9 +7,9 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Input } from "@components/UI/Input.js";
import { Label } from "@components/UI/Label.js";
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
import { Protobuf } from "@meshtastic/js";
import { useForm } from "react-hook-form";
+7 -7
View File
@@ -1,10 +1,10 @@
import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.js";
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.js";
import { ImportDialog } from "@components/Dialog/ImportDialog.js";
import { QRDialog } from "@components/Dialog/QRDialog.js";
import { RebootDialog } from "@components/Dialog/RebootDialog.js";
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.tsx";
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.tsx";
import { ImportDialog } from "@components/Dialog/ImportDialog.tsx";
import { QRDialog } from "@components/Dialog/QRDialog.tsx";
import { RebootDialog } from "@components/Dialog/RebootDialog.tsx";
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
export const DialogManager = (): JSX.Element => {
const { channels, config, dialog, setDialogOpen } = useDevice();
+7 -7
View File
@@ -1,5 +1,5 @@
import { Button } from "@components/UI/Button.js";
import { Checkbox } from "@components/UI/Checkbox.js";
import { Button } from "@components/UI/Button.tsx";
import { Checkbox } from "@components/UI/Checkbox.tsx";
import {
Dialog,
DialogContent,
@@ -7,11 +7,11 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Input } from "@components/UI/Input.js";
import { Label } from "@components/UI/Label.js";
import { Switch } from "@components/UI/Switch.js";
import { useDevice } from "@core/stores/deviceStore.js";
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
import { Switch } from "@components/UI/Switch.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
import { toByteArray } from "base64-js";
import { useEffect, useState } from "react";
+7 -7
View File
@@ -1,20 +1,20 @@
import { BLE } from "@components/PageComponents/Connect/BLE.js";
import { HTTP } from "@components/PageComponents/Connect/HTTP.js";
import { Serial } from "@components/PageComponents/Connect/Serial.js";
import { BLE } from "@components/PageComponents/Connect/BLE.tsx";
import { HTTP } from "@components/PageComponents/Connect/HTTP.tsx";
import { Serial } from "@components/PageComponents/Connect/Serial.tsx";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
} from "@components/UI/Dialog.tsx";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@components/UI/Tabs.js";
import { Link } from "@components/UI/Typography/Link.js";
import { Subtle } from "@components/UI/Typography/Subtle.js";
} from "@components/UI/Tabs.tsx";
import { Link } from "@components/UI/Typography/Link.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
export interface TabElementProps {
closeDialog: () => void;
@@ -1,4 +1,4 @@
import { Button } from "@components/UI/Button.js";
import { Button } from "@components/UI/Button.tsx";
import {
Dialog,
DialogContent,
@@ -6,7 +6,7 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
} from "@components/UI/Dialog.tsx";
export interface PkiRegenerateDialogProps {
open: boolean;
+4 -4
View File
@@ -1,4 +1,4 @@
import { Checkbox } from "@components/UI/Checkbox.js";
import { Checkbox } from "@components/UI/Checkbox.tsx";
import {
Dialog,
DialogContent,
@@ -6,9 +6,9 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Input } from "@components/UI/Input.js";
import { Label } from "@components/UI/Label.js";
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
import { Protobuf, type Types } from "@meshtastic/js";
import { fromByteArray } from "base64-js";
import { ClipboardIcon } from "lucide-react";
+4 -4
View File
@@ -1,13 +1,13 @@
import { Button } from "@components/UI/Button.js";
import { Button } from "@components/UI/Button.tsx";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Input } from "@components/UI/Input.js";
import { useDevice } from "@core/stores/deviceStore.js";
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { ClockIcon, RefreshCwIcon } from "lucide-react";
import { useState } from "react";
+4 -4
View File
@@ -1,6 +1,6 @@
import { useAppStore } from "@app/core/stores/appStore";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { Button } from "@components/UI/Button.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import { Button } from "@components/UI/Button.tsx";
import {
Dialog,
DialogContent,
@@ -8,8 +8,8 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Label } from "@components/UI/Label.js";
} from "@components/UI/Dialog.tsx";
import { Label } from "@components/UI/Label.tsx";
export interface RemoveNodeDialogProps {
open: boolean;
+4 -4
View File
@@ -1,13 +1,13 @@
import { Button } from "@components/UI/Button.js";
import { Button } from "@components/UI/Button.tsx";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.js";
import { Input } from "@components/UI/Input.js";
import { useDevice } from "@core/stores/deviceStore.js";
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { ClockIcon, PowerIcon } from "lucide-react";
import { useState } from "react";
+5 -5
View File
@@ -1,11 +1,11 @@
import {
DynamicFormField,
type FieldProps,
} from "@components/Form/DynamicFormField.js";
import { FieldWrapper } from "@components/Form/FormWrapper.js";
import { Button } from "@components/UI/Button.js";
import { H4 } from "@components/UI/Typography/H4.js";
import { Subtle } from "@components/UI/Typography/Subtle.js";
} from "@components/Form/DynamicFormField.tsx";
import { FieldWrapper } from "@components/Form/FormWrapper.tsx";
import { Button } from "@components/UI/Button.tsx";
import { H4 } from "@components/UI/Typography/H4.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import {
type Control,
type DefaultValues,
+4 -4
View File
@@ -1,19 +1,19 @@
import {
GenericInput,
type InputFieldProps,
} from "@components/Form/FormInput.js";
} from "@components/Form/FormInput.tsx";
import {
PasswordGenerator,
type PasswordGeneratorProps,
} from "@components/Form/FormPasswordGenerator.js";
} from "@components/Form/FormPasswordGenerator.tsx";
import {
type SelectFieldProps,
SelectInput,
} from "@components/Form/FormSelect.js";
} from "@components/Form/FormSelect.tsx";
import {
type ToggleFieldProps,
ToggleInput,
} from "@components/Form/FormToggle.js";
} from "@components/Form/FormToggle.tsx";
import type { Control, FieldValues } from "react-hook-form";
export type FieldProps<T> =
+20 -3
View File
@@ -1,10 +1,12 @@
import type {
BaseFormBuilderProps,
GenericFormElementProps,
} from "@components/Form/DynamicForm.js";
import { Input } from "@components/UI/Input.js";
} from "@components/Form/DynamicForm.tsx";
import { Input } from "@components/UI/Input.tsx";
import type { LucideIcon } from "lucide-react";
import { Eye, EyeOff } from "lucide-react";
import type { ChangeEventHandler } from "react";
import { useState } from "react";
import { Controller, type FieldValues } from "react-hook-form";
export interface InputFieldProps<T> extends BaseFormBuilderProps<T> {
@@ -27,13 +29,28 @@ export function GenericInput<T extends FieldValues>({
disabled,
field,
}: GenericFormElementProps<T, InputFieldProps<T>>) {
const [passwordShown, setPasswordShown] = useState(false);
const togglePasswordVisiblity = () => {
setPasswordShown(!passwordShown);
};
return (
<Controller
name={field.name}
control={control}
render={({ field: { value, onChange, ...rest } }) => (
<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}
value={field.type === "number" ? Number.parseFloat(value) : value}
onChange={(e) => {
+18 -3
View File
@@ -1,9 +1,11 @@
import type {
BaseFormBuilderProps,
GenericFormElementProps,
} from "@components/Form/DynamicForm.js";
import { Generator } from "@components/UI/Generator.js";
} from "@components/Form/DynamicForm.tsx";
import { Generator } from "@components/UI/Generator.tsx";
import { Eye, EyeOff } from "lucide-react";
import type { ChangeEventHandler, MouseEventHandler } from "react";
import { useState } from "react";
import { Controller, type FieldValues } from "react-hook-form";
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
@@ -21,13 +23,26 @@ export function PasswordGenerator<T extends FieldValues>({
field,
disabled,
}: GenericFormElementProps<T, PasswordGeneratorProps<T>>) {
const [passwordShown, setPasswordShown] = useState(false);
const togglePasswordVisiblity = () => {
setPasswordShown(!passwordShown);
};
return (
<Controller
name={field.name}
control={control}
render={({ field: { value, ...rest } }) => (
<Generator
hide={field.hide}
type={field.hide && !passwordShown ? "password" : "text"}
action={
field.hide
? {
icon: passwordShown ? EyeOff : Eye,
onClick: togglePasswordVisiblity,
}
: undefined
}
devicePSKBitCount={field.devicePSKBitCount}
bits={field.bits}
inputChange={field.inputChange}
+2 -2
View File
@@ -1,14 +1,14 @@
import type {
BaseFormBuilderProps,
GenericFormElementProps,
} from "@components/Form/DynamicForm.js";
} from "@components/Form/DynamicForm.tsx";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@components/UI/Select.js";
} from "@components/UI/Select.tsx";
import { Controller, type FieldValues } from "react-hook-form";
export interface SelectFieldProps<T> extends BaseFormBuilderProps<T> {
+2 -2
View File
@@ -1,8 +1,8 @@
import type {
BaseFormBuilderProps,
GenericFormElementProps,
} from "@components/Form/DynamicForm.js";
import { Switch } from "@components/UI/Switch.js";
} from "@components/Form/DynamicForm.tsx";
import { Switch } from "@components/UI/Switch.tsx";
import type { ChangeEvent } from "react";
import { Controller, type FieldValues } from "react-hook-form";
+1 -1
View File
@@ -1,4 +1,4 @@
import { Label } from "@components/UI/Label.js";
import { Label } from "@components/UI/Label.tsx";
export interface FieldWrapperProps {
label: string;
+10 -5
View File
@@ -1,7 +1,7 @@
import type { ChannelValidation } from "@app/validation/channel.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useToast } from "@core/hooks/useToast.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { ChannelValidation } from "@app/validation/channel.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useToast } from "@core/hooks/useToast.ts";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
import { fromByteArray, toByteArray } from "base64-js";
import cryptoRandomString from "crypto-random-string";
@@ -111,10 +111,14 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
type: "select",
name: "role",
label: "Role",
disabled: channel.index === 0,
description:
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
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,
selectChange: selectChangeEvent,
buttonClick: clickEvent,
hide: true,
properties: {
value: pass,
},
@@ -1,6 +1,6 @@
import type { BluetoothValidation } from "@app/validation/config/bluetooth.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { BluetoothValidation } from "@app/validation/config/bluetooth.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Bluetooth = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { DeviceValidation } from "@app/validation/config/device.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { DeviceValidation } from "@app/validation/config/device.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Device = (): JSX.Element => {
@@ -32,7 +32,22 @@ export const Device = (): JSX.Element => {
label: "Role",
description: "What role the device performs on the mesh",
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,
},
},
@@ -1,6 +1,6 @@
import type { DisplayValidation } from "@app/validation/config/display.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { DisplayValidation } from "@app/validation/config/display.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Display = (): JSX.Element => {
+10 -3
View File
@@ -1,6 +1,6 @@
import type { LoRaValidation } from "@app/validation/config/lora.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { LoRaValidation } from "@app/validation/config/lora.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const LoRa = (): JSX.Element => {
@@ -56,6 +56,13 @@ export const LoRa = (): JSX.Element => {
label: "Ignore MQTT",
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 { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { NetworkValidation } from "@app/validation/config/network.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import {
convertIntToIpAddress,
convertIpAddressToInt,
} from "@core/utils/ip.ts";
import { Protobuf } from "@meshtastic/js";
export const Network = (): JSX.Element => {
@@ -13,9 +17,12 @@ export const Network = (): JSX.Element => {
case: "network",
value: {
...data,
ipv4Config: new Protobuf.Config.Config_NetworkConfig_IpV4Config(
data.ipv4Config,
),
ipv4Config: new Protobuf.Config.Config_NetworkConfig_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 (
<DynamicForm<NetworkValidation>
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={[
{
label: "WiFi Config",
@@ -1,6 +1,6 @@
import type { PositionValidation } from "@app/validation/config/position.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { PositionValidation } from "@app/validation/config/position.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Position = (): JSX.Element => {
@@ -77,12 +77,6 @@ export const Position = (): JSX.Element => {
label: "Enable Pin",
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 { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { PowerValidation } from "@app/validation/config/power.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Power = (): JSX.Element => {
@@ -1,11 +1,11 @@
import { PkiRegenerateDialog } from "@app/components/Dialog/PkiRegenerateDialog";
import { DynamicForm } from "@app/components/Form/DynamicForm.js";
import { DynamicForm } from "@app/components/Form/DynamicForm.tsx";
import {
getX25519PrivateKey,
getX25519PublicKey,
} from "@app/core/utils/x25519";
import type { SecurityValidation } from "@app/validation/config/security.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { SecurityValidation } from "@app/validation/config/security.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
import { fromByteArray, toByteArray } from "base64-js";
import { Eye, EyeOff } from "lucide-react";
@@ -27,7 +27,7 @@ export const Security = (): JSX.Element => {
fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
);
const [adminKey, setAdminKey] = useState<string>(
fromByteArray(config.security?.adminKey ?? new Uint8Array(0)),
fromByteArray(config.security?.adminKey[0] ?? new Uint8Array(0)),
);
const [adminKeyValidationText, setAdminKeyValidationText] =
useState<string>();
@@ -42,7 +42,7 @@ export const Security = (): JSX.Element => {
case: "security",
value: {
...data,
adminKey: toByteArray(adminKey),
adminKey: [toByteArray(adminKey)],
privateKey: toByteArray(privateKey),
publicKey: toByteArray(publicKey),
},
@@ -129,8 +129,6 @@ export const Security = (): JSX.Element => {
publicKey: publicKey,
adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
isManaged: config.security?.isManaged ?? false,
bluetoothLoggingEnabled:
config.security?.bluetoothLoggingEnabled ?? false,
debugLogApiEnabled: config.security?.debugLogApiEnabled ?? false,
serialEnabled: config.security?.serialEnabled ?? false,
},
@@ -212,18 +210,12 @@ export const Security = (): JSX.Element => {
label: "Logging Settings",
description: "Settings for Logging",
fields: [
{
type: "toggle",
name: "bluetoothLoggingEnabled",
label: "Allow Bluetooth Logging",
description:
"Enables device (serial style logs) over Bluetooth",
},
{
type: "toggle",
name: "debugLogApiEnabled",
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",
@@ -1,10 +1,10 @@
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
import { Button } from "@components/UI/Button.js";
import { Mono } from "@components/generic/Mono.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { subscribeAll } from "@core/subscriptions.js";
import { randId } from "@core/utils/randId.js";
import { Button } from "@components/UI/Button.tsx";
import { Mono } from "@components/generic/Mono.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts";
import { BleConnection, Constants } from "@meshtastic/js";
import { useCallback, useEffect, useState } from "react";
@@ -1,12 +1,12 @@
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
import { Button } from "@components/UI/Button.js";
import { Input } from "@components/UI/Input.js";
import { Label } from "@components/UI/Label.js";
import { Switch } from "@components/UI/Switch.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { subscribeAll } from "@core/subscriptions.js";
import { randId } from "@core/utils/randId.js";
import { Button } from "@components/UI/Button.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
import { Switch } from "@components/UI/Switch.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts";
import { HttpConnection } from "@meshtastic/js";
import { useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
@@ -1,10 +1,10 @@
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
import { Button } from "@components/UI/Button.js";
import { Mono } from "@components/generic/Mono.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { subscribeAll } from "@core/subscriptions.js";
import { randId } from "@core/utils/randId.js";
import { Button } from "@components/UI/Button.tsx";
import { Mono } from "@components/generic/Mono.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts";
import { SerialConnection } from "@meshtastic/js";
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 {
type MessageWithState,
useDevice,
} from "@app/core/stores/deviceStore.js";
import { Message } from "@components/PageComponents/Messages/Message.js";
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.js";
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.js";
} from "@app/core/stores/deviceStore.ts";
import { Message } from "@components/PageComponents/Messages/Message.tsx";
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.tsx";
import type { Protobuf, Types } from "@meshtastic/js";
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 type { Protobuf } from "@meshtastic/js";
import {
@@ -1,6 +1,6 @@
import { Button } from "@components/UI/Button.js";
import { Input } from "@components/UI/Input.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Button } from "@components/UI/Button.tsx";
import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import type { Types } from "@meshtastic/js";
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 { numberToHexUnpadded } from "@noble/curves/abstract/utils";
export interface TraceRouteProps {
from?: Protobuf.Mesh.NodeInfo;
@@ -24,7 +25,10 @@ export const TraceRoute = ({
<div className="ml-5 flex">
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
{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}
</span>
</div>
@@ -1,6 +1,6 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { Protobuf } from "@meshtastic/js";
export const AmbientLighting = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { AudioValidation } from "@app/validation/moduleConfig/audio.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { AudioValidation } from "@app/validation/moduleConfig/audio.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Audio = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const CannedMessage = (): JSX.Element => {
@@ -1,6 +1,6 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { Protobuf } from "@meshtastic/js";
export const DetectionSensor = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const ExternalNotification = (): JSX.Element => {
@@ -1,6 +1,6 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { Protobuf } from "@meshtastic/js";
export const MQTT = (): JSX.Element => {
@@ -1,6 +1,6 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { Protobuf } from "@meshtastic/js";
export const NeighborInfo = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Paxcounter = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const RangeTest = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { SerialValidation } from "@app/validation/moduleConfig/serial.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { SerialValidation } from "@app/validation/moduleConfig/serial.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Serial = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.ts";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const StoreForward = (): JSX.Element => {
@@ -1,6 +1,6 @@
import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.js";
import { DynamicForm } from "@components/Form/DynamicForm.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
export const Telemetry = (): JSX.Element => {
+3 -2
View File
@@ -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 Footer from "./UI/Footer";
@@ -8,6 +8,7 @@ export interface PageLayoutProps {
children: React.ReactNode;
actions?: {
icon: LucideIcon;
iconClasses?: string;
onClick: () => void;
}[];
}
@@ -39,7 +40,7 @@ export const PageLayout = ({
className="transition-all hover:text-accent"
onClick={action.onClick}
>
<action.icon />
<action.icon className={action.iconClasses} />
</button>
))}
</div>
+5 -5
View File
@@ -1,8 +1,8 @@
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
import { Subtle } from "@components/UI/Typography/Subtle.js";
import { useDevice } from "@core/stores/deviceStore.js";
import type { Page } from "@core/stores/deviceStore.js";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import type { Page } from "@core/stores/deviceStore.ts";
import {
BatteryMediumIcon,
CpuIcon,
+2 -2
View File
@@ -1,4 +1,4 @@
import { useToast } from "@core/hooks/useToast.js";
import { useToast } from "@core/hooks/useToast.ts";
import {
Toast,
@@ -7,7 +7,7 @@ import {
ToastProvider,
ToastTitle,
ToastViewport,
} from "@components/UI/Toast.js";
} from "@components/UI/Toast.tsx";
export function Toaster() {
const { toasts } = useToast();
+1 -1
View File
@@ -1,7 +1,7 @@
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
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",
+1 -1
View File
@@ -2,7 +2,7 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { Check } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
+2 -2
View File
@@ -3,8 +3,8 @@ import { Command as CommandPrimitive } from "cmdk";
import { Search } from "lucide-react";
import * as React from "react";
import { Dialog, DialogContent } from "@components/UI/Dialog.js";
import { cn } from "@core/utils/cn.js";
import { Dialog, DialogContent } from "@components/UI/Dialog.tsx";
import { cn } from "@core/utils/cn.ts";
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
+1 -1
View File
@@ -2,7 +2,7 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Dialog = DialogPrimitive.Root;
+1 -1
View File
@@ -2,7 +2,7 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const DropdownMenu = DropdownMenuPrimitive.Root;
+6 -6
View File
@@ -1,18 +1,18 @@
import * as React from "react";
import { Button } from "@components/UI/Button.js";
import { Input } from "@components/UI/Input.js";
import { Button } from "@components/UI/Button.tsx";
import { Input } from "@components/UI/Input.tsx";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@components/UI/Select.js";
} from "@components/UI/Select.tsx";
import type { LucideIcon } from "lucide-react";
export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
hide?: boolean;
type: "text" | "password";
devicePSKBitCount?: number;
value: string;
variant: "default" | "invalid";
@@ -31,7 +31,7 @@ export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
(
{
hide = true,
type,
devicePSKBitCount,
variant,
value,
@@ -68,7 +68,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
return (
<>
<Input
type={hide ? "password" : "text"}
type={type}
id="pskInput"
variant={variant}
value={value}
+1 -1
View File
@@ -1,6 +1,6 @@
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 { LucideIcon } from "lucide-react";
+1 -1
View File
@@ -1,7 +1,7 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
+1 -1
View File
@@ -2,7 +2,7 @@ import * as MenubarPrimitive from "@radix-ui/react-menubar";
import { Check, ChevronRight, Circle } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const MenubarMenu = MenubarPrimitive.Menu;
+1 -1
View File
@@ -1,7 +1,7 @@
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Popover = PopoverPrimitive.Root;
+1 -1
View File
@@ -1,7 +1,7 @@
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
+1 -1
View File
@@ -2,7 +2,7 @@ import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Select = SelectPrimitive.Root;
+1 -1
View File
@@ -1,7 +1,7 @@
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
+1 -1
View File
@@ -1,4 +1,4 @@
import { H4 } from "@components/UI/Typography/H4.js";
import { H4 } from "@components/UI/Typography/H4.tsx";
export interface SidebarSectionProps {
label: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { Button } from "@components/UI/Button.js";
import { Button } from "@components/UI/Button.tsx";
import type { LucideIcon } from "lucide-react";
export interface SidebarButtonProps {
+1 -1
View File
@@ -1,7 +1,7 @@
import * as SwitchPrimitives from "@radix-ui/react-switch";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
+1 -1
View File
@@ -1,7 +1,7 @@
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const Tabs = TabsPrimitive.Root;
+1 -1
View File
@@ -3,7 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const ToastProvider = ToastPrimitives.Provider;
+1 -1
View File
@@ -1,7 +1,7 @@
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "@core/utils/cn.js";
import { cn } from "@core/utils/cn.ts";
const TooltipProvider = TooltipPrimitive.Provider;
+1 -1
View File
@@ -1,4 +1,4 @@
import { cn } from "@app/core/utils/cn.js";
import { cn } from "@app/core/utils/cn.ts";
export interface H4Props {
className?: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { cn } from "@app/core/utils/cn.js";
import { cn } from "@app/core/utils/cn.ts";
export interface SubtleProps {
className?: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { useAppStore } from "@core/stores/appStore.js";
import { useAppStore } from "@core/stores/appStore.ts";
import type { ReactNode } from "react";
export interface ThemeControllerProps {
+4 -4
View File
@@ -1,6 +1,6 @@
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_REMOVE_DELAY = 1000000;
@@ -80,7 +80,7 @@ export const reducer = (state: State, action: Action): State => {
return {
...state,
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,
open: false,
}
: t,
: t
),
};
}
@@ -190,4 +190,4 @@ function useToast() {
};
}
export { useToast, toast };
export { toast, useToast };
+1 -1
View File
@@ -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";
export const subscribeAll = (
+14
View File
@@ -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
View File
@@ -4,7 +4,7 @@ import "maplibre-gl/dist/maplibre-gl.css";
import { StrictMode } from "react";
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 root = createRoot(container);
+5 -5
View File
@@ -3,11 +3,11 @@ import {
TabsContent,
TabsList,
TabsTrigger,
} from "@app/components/UI/Tabs.js";
import { Channel } from "@components/PageComponents/Channel.js";
import { PageLayout } from "@components/PageLayout.js";
import { Sidebar } from "@components/Sidebar.js";
import { useDevice } from "@core/stores/deviceStore.js";
} from "@app/components/UI/Tabs.tsx";
import { Channel } from "@components/PageComponents/Channel.tsx";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Types } from "@meshtastic/js";
import type { Protobuf } from "@meshtastic/js";
import { ImportIcon, QrCodeIcon } from "lucide-react";
+10 -10
View File
@@ -1,18 +1,18 @@
import { Bluetooth } from "@components/PageComponents/Config/Bluetooth.js";
import { Device } from "@components/PageComponents/Config/Device.js";
import { Display } from "@components/PageComponents/Config/Display.js";
import { LoRa } from "@components/PageComponents/Config/LoRa.js";
import { Network } from "@components/PageComponents/Config/Network.js";
import { Position } from "@components/PageComponents/Config/Position.js";
import { Power } from "@components/PageComponents/Config/Power.js";
import { Security } from "@components/PageComponents/Config/Security.js";
import { Bluetooth } from "@components/PageComponents/Config/Bluetooth.tsx";
import { Device } from "@components/PageComponents/Config/Device.tsx";
import { Display } from "@components/PageComponents/Config/Display.tsx";
import { LoRa } from "@components/PageComponents/Config/LoRa.tsx";
import { Network } from "@components/PageComponents/Config/Network.tsx";
import { Position } from "@components/PageComponents/Config/Position.tsx";
import { Power } from "@components/PageComponents/Config/Power.tsx";
import { Security } from "@components/PageComponents/Config/Security.tsx";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@components/UI/Tabs.js";
import { useDevice } from "@core/stores/deviceStore.js";
} from "@components/UI/Tabs.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
export const DeviceConfig = (): JSX.Element => {
const { metadata } = useDevice();
+13 -13
View File
@@ -1,21 +1,21 @@
import { AmbientLighting } from "@app/components/PageComponents/ModuleConfig/AmbientLighting.js";
import { DetectionSensor } from "@app/components/PageComponents/ModuleConfig/DetectionSensor.js";
import { NeighborInfo } from "@app/components/PageComponents/ModuleConfig/NeighborInfo.js";
import { Audio } from "@components/PageComponents/ModuleConfig/Audio.js";
import { CannedMessage } from "@components/PageComponents/ModuleConfig/CannedMessage.js";
import { ExternalNotification } from "@components/PageComponents/ModuleConfig/ExternalNotification.js";
import { MQTT } from "@components/PageComponents/ModuleConfig/MQTT.js";
import { Paxcounter } from "@components/PageComponents/ModuleConfig/Paxcounter.js";
import { RangeTest } from "@components/PageComponents/ModuleConfig/RangeTest.js";
import { Serial } from "@components/PageComponents/ModuleConfig/Serial.js";
import { StoreForward } from "@components/PageComponents/ModuleConfig/StoreForward.js";
import { Telemetry } from "@components/PageComponents/ModuleConfig/Telemetry.js";
import { AmbientLighting } from "@app/components/PageComponents/ModuleConfig/AmbientLighting.tsx";
import { DetectionSensor } from "@app/components/PageComponents/ModuleConfig/DetectionSensor.tsx";
import { NeighborInfo } from "@app/components/PageComponents/ModuleConfig/NeighborInfo.tsx";
import { Audio } from "@components/PageComponents/ModuleConfig/Audio.tsx";
import { CannedMessage } from "@components/PageComponents/ModuleConfig/CannedMessage.tsx";
import { ExternalNotification } from "@components/PageComponents/ModuleConfig/ExternalNotification.tsx";
import { MQTT } from "@components/PageComponents/ModuleConfig/MQTT.tsx";
import { Paxcounter } from "@components/PageComponents/ModuleConfig/Paxcounter.tsx";
import { RangeTest } from "@components/PageComponents/ModuleConfig/RangeTest.tsx";
import { Serial } from "@components/PageComponents/ModuleConfig/Serial.tsx";
import { StoreForward } from "@components/PageComponents/ModuleConfig/StoreForward.tsx";
import { Telemetry } from "@components/PageComponents/ModuleConfig/Telemetry.tsx";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@components/UI/Tabs.js";
} from "@components/UI/Tabs.tsx";
export const ModuleConfig = (): JSX.Element => {
const tabs = [
+8 -8
View File
@@ -1,11 +1,11 @@
import { useDevice } from "@app/core/stores/deviceStore.js";
import { PageLayout } from "@components/PageLayout.js";
import { Sidebar } from "@components/Sidebar.js";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
import { useToast } from "@core/hooks/useToast.js";
import { DeviceConfig } from "@pages/Config/DeviceConfig.js";
import { ModuleConfig } from "@pages/Config/ModuleConfig.js";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useToast } from "@core/hooks/useToast.ts";
import { DeviceConfig } from "@pages/Config/DeviceConfig.tsx";
import { ModuleConfig } from "@pages/Config/ModuleConfig.tsx";
import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react";
import { useState } from "react";
+6 -6
View File
@@ -1,9 +1,9 @@
import { useAppStore } from "@app/core/stores/appStore.js";
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
import { Button } from "@components/UI/Button.js";
import { Separator } from "@components/UI/Seperator.js";
import { H3 } from "@components/UI/Typography/H3.js";
import { Subtle } from "@components/UI/Typography/Subtle.js";
import { useAppStore } from "@app/core/stores/appStore.ts";
import { useDeviceStore } from "@app/core/stores/deviceStore.ts";
import { Button } from "@components/UI/Button.tsx";
import { Separator } from "@components/UI/Seperator.tsx";
import { H3 } from "@components/UI/Typography/H3.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import {
BluetoothIcon,
ListPlusIcon,
+11 -9
View File
@@ -1,12 +1,13 @@
import { Subtle } from "@app/components/UI/Typography/Subtle.js";
import { cn } from "@app/core/utils/cn.js";
import { PageLayout } from "@components/PageLayout.js";
import { Sidebar } from "@components/Sidebar.js";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import { cn } from "@app/core/utils/cn.ts";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { bbox, lineString } from "@turf/turf";
import {
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">
<Hashicon value={node.num.toString()} size={22} />
<Subtle className={cn(zoom < 12 && "hidden")}>
{node.user?.longName}
{node.user?.longName ||
`!${numberToHexUnpadded(node.num)}`}
</Subtle>
</div>
</Marker>
+31 -11
View File
@@ -1,14 +1,15 @@
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.js";
import { PageLayout } from "@components/PageLayout.js";
import { Sidebar } from "@components/Sidebar.js";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
import { useToast } from "@core/hooks/useToast.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.tsx";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useToast } from "@core/hooks/useToast.ts";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf, Types } from "@meshtastic/js";
import { getChannelName } from "@pages/Channels.js";
import { HashIcon, WaypointsIcon } from "lucide-react";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { getChannelName } from "@pages/Channels.tsx";
import { HashIcon, LockIcon, LockOpenIcon, WaypointsIcon } from "lucide-react";
import { useState } from "react";
export const MessagesPage = (): JSX.Element => {
@@ -28,6 +29,8 @@ export const MessagesPage = (): JSX.Element => {
);
const currentChannel = channels.get(activeChat);
const { toast } = useToast();
const node = nodes.get(activeChat);
const nodeHex = node?.num ? numberToHexUnpadded(node.num) : "Unknown";
return (
<>
@@ -56,7 +59,7 @@ export const MessagesPage = (): JSX.Element => {
{filteredNodes.map((node) => (
<SidebarButton
key={node.num}
label={node.user?.longName ?? "Unknown"}
label={node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`}
active={activeChat === node.num}
onClick={() => {
setChatType("direct");
@@ -73,12 +76,29 @@ export const MessagesPage = (): JSX.Element => {
chatType === "broadcast" && currentChannel
? getChannelName(currentChannel)
: chatType === "direct" && nodes.get(activeChat)
? nodes.get(activeChat)?.user?.longName ?? "Unknown"
? nodes.get(activeChat)?.user?.longName ?? nodeHex
: "Loading..."
}`}
actions={
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,
async onClick() {
+17 -8
View File
@@ -1,14 +1,15 @@
import Footer from "@app/components/UI/Footer";
import { useAppStore } from "@app/core/stores/appStore";
import { Sidebar } from "@components/Sidebar.js";
import { Button } from "@components/UI/Button.js";
import { Mono } from "@components/generic/Mono.js";
import { Table } from "@components/generic/Table/index.js";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Sidebar } from "@components/Sidebar.tsx";
import { Button } from "@components/UI/Button.tsx";
import { Mono } from "@components/generic/Mono.tsx";
import { Table } from "@components/generic/Table/index.tsx";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
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 { base16 } from "rfc4648";
@@ -38,6 +39,7 @@ export const NodesPage = (): JSX.Element => {
{ title: "MAC Address", type: "normal", sortable: true },
{ title: "Last Heard", type: "normal", sortable: true },
{ title: "SNR", type: "normal", sortable: true },
{ title: "Encryption", type: "normal", sortable: false },
{ title: "Connection", type: "normal", sortable: true },
{ title: "Remove", type: "normal", sortable: false },
]}
@@ -49,7 +51,7 @@ export const NodesPage = (): JSX.Element => {
? `Meshtastic ${base16
.stringify(node.user?.macaddr.subarray(4, 6) ?? [])
.toLowerCase()}`
: `UNK: ${node.num}`)}
: `!${numberToHexUnpadded(node.num)}`)}
</h1>,
<Mono key="model">
@@ -73,6 +75,13 @@ export const NodesPage = (): JSX.Element => {
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
{(node.snr + 10) * 5}raw
</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">
{node.lastHeard !== 0
? node.viaMqtt === false && node.hopsAway === 0
+3
View File
@@ -60,4 +60,7 @@ export class LoRaValidation
@IsBoolean()
ignoreMqtt: boolean;
@IsBoolean()
configOkToMqtt: boolean;
}
+8 -5
View File
@@ -41,21 +41,24 @@ export class NetworkValidation
export class NetworkValidationIpV4Config
implements
Omit<Protobuf.Config.Config_NetworkConfig_IpV4Config, keyof Message>
Omit<
Protobuf.Config.Config_NetworkConfig_IpV4Config,
keyof Message | "ip" | "gateway" | "subnet" | "dns"
>
{
@IsIP()
@IsOptional()
ip: number;
ip: string;
@IsIP()
@IsOptional()
gateway: number;
gateway: string;
@IsIP()
@IsOptional()
subnet: number;
subnet: string;
@IsIP()
@IsOptional()
dns: number;
dns: string;
}
-3
View File
@@ -43,7 +43,4 @@ export class PositionValidation
@IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode)
gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode;
@IsArray()
channelPrecision: number[];
}
+1 -1
View File
@@ -1,6 +1,6 @@
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 {
@IsArray()
+2 -1
View File
@@ -34,6 +34,7 @@
"@types/w3c-web-serial"
],
"strictPropertyInitialization": false,
"experimentalDecorators": true
"experimentalDecorators": true,
"allowImportingTsExtensions": true,
}
}
+1 -2
View File
@@ -1,11 +1,10 @@
import { execSync } from "node:child_process";
import { resolve } from "node:path";
import react from "@vitejs/plugin-react";
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vite";
import EnvironmentPlugin from "vite-plugin-environment";
import react from "@vitejs/plugin-react";
let hash = "";
try {