Merge branch 'master' of github.com:meshtastic/meshtastic-web
This commit is contained in:
+3
-3
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||||
|
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="site.webmanifest" />
|
||||||
<link rel="mask-icon" href="/Logo_Black.svg" color="#67ea94" />
|
<link rel="mask-icon" href="Logo_Black.svg" color="#67ea94" />
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
@@ -26,6 +26,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script type="module" src="/src/index.tsx"></script>
|
<script type="module" src="src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+11
-14
@@ -8,8 +8,7 @@
|
|||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite 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/)",
|
||||||
"format": "prettier --write 'src/**/*.{ts,tsx}' && eslint src/*.{ts,tsx}",
|
"format": "prettier --write 'src/**/*.{ts,tsx}' && eslint src/*.{ts,tsx}"
|
||||||
"check": "unimported"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -20,35 +19,34 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://meshtastic.org",
|
"homepage": "https://meshtastic.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@arcgis/core": "^4.24.7",
|
|
||||||
"@emeraldpay/hashicon-react": "^0.5.2",
|
"@emeraldpay/hashicon-react": "^0.5.2",
|
||||||
"@hookform/resolvers": "^2.9.7",
|
"@hookform/resolvers": "^2.9.7",
|
||||||
"@meshtastic/eslint-config": "^1.0.8",
|
"@meshtastic/eslint-config": "^1.0.8",
|
||||||
"@meshtastic/meshtasticjs": "^0.6.88",
|
"@meshtastic/meshtasticjs": "^0.6.92",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"evergreen-ui": "^6.10.3",
|
"evergreen-ui": "^6.10.3",
|
||||||
"geodesy": "^2.4.0",
|
"geodesy": "^2.4.0",
|
||||||
"immer": "^9.0.15",
|
"immer": "^9.0.15",
|
||||||
|
"mapbox-gl": "npm:empty-npm-package@1.0.0",
|
||||||
|
"maplibre-gl": "^2.3.0",
|
||||||
"modern-css-reset": "^1.4.0",
|
"modern-css-reset": "^1.4.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.34.0",
|
"react-hook-form": "^7.34.2",
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.4.0",
|
||||||
"react-json-pretty": "^2.2.0",
|
"react-json-pretty": "^2.2.0",
|
||||||
"react-qrcode-logo": "^2.7.0",
|
"react-map-gl": "^7.0.19",
|
||||||
|
"react-qrcode-logo": "^2.8.0",
|
||||||
"rfc4648": "^1.5.2",
|
"rfc4648": "^1.5.2",
|
||||||
"snarkdown": "^2.0.0",
|
"zustand": "4.1.0"
|
||||||
"swr": "^1.3.0",
|
|
||||||
"vite-plugin-environment": "^1.1.2",
|
|
||||||
"zustand": "4.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chrome": "^0.0.193",
|
"@types/chrome": "^0.0.193",
|
||||||
"@types/geodesy": "^2.2.3",
|
"@types/geodesy": "^2.2.3",
|
||||||
"@types/node": "^18.7.1",
|
"@types/node": "^18.7.6",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "^18.0.17",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/w3c-web-serial": "^1.0.2",
|
"@types/w3c-web-serial": "^1.0.2",
|
||||||
@@ -59,8 +57,7 @@
|
|||||||
"tar": "^6.1.11",
|
"tar": "^6.1.11",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"unimported": "^1.21.0",
|
"vite": "^3.0.8",
|
||||||
"vite": "^3.0.6",
|
"vite-plugin-environment": "^1.1.2"
|
||||||
"vite-plugin-cdn-import": "^0.3.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+419
-895
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -1,6 +1,7 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { Pane } from "evergreen-ui";
|
import { Pane } from "evergreen-ui";
|
||||||
|
import { MapProvider } from "react-map-gl";
|
||||||
|
|
||||||
import { AppLayout } from "@components/layout/AppLayout.js";
|
import { AppLayout } from "@components/layout/AppLayout.js";
|
||||||
|
|
||||||
@@ -10,7 +11,9 @@ export const App = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<Pane display="flex">
|
<Pane display="flex">
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<PageRouter />
|
<MapProvider>
|
||||||
|
<PageRouter />
|
||||||
|
</MapProvider>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
</Pane>
|
</Pane>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { Device, DeviceContext } from "./core/stores/deviceStore.js";
|
import { DeviceContext } from "./core/providers/useDevice.js";
|
||||||
|
import type { Device } from "./core/stores/deviceStore.js";
|
||||||
|
|
||||||
export interface DeviceProps {
|
export interface DeviceProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|||||||
+4
-2
@@ -1,10 +1,12 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { useDevice } from "./core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
|
||||||
import { ChannelsPage } from "./pages/Channels/index.js";
|
import { ChannelsPage } from "./pages/Channels/index.js";
|
||||||
import { ConfigPage } from "./pages/Config/index.js";
|
import { ConfigPage } from "./pages/Config/index.js";
|
||||||
import { ExtensionsPage } from "./pages/Extensions/Index.js";
|
import { ExtensionsPage } from "./pages/Extensions/Index.js";
|
||||||
import { InfoPage } from "./pages/Info/index.js";
|
import { InfoPage } from "./pages/Info/index.js";
|
||||||
|
import { MapPage } from "./pages/Map/index.js";
|
||||||
import { MessagesPage } from "./pages/Messages/index.js";
|
import { MessagesPage } from "./pages/Messages/index.js";
|
||||||
|
|
||||||
export const PageRouter = (): JSX.Element => {
|
export const PageRouter = (): JSX.Element => {
|
||||||
@@ -12,7 +14,7 @@ export const PageRouter = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{activePage === "messages" && <MessagesPage />}
|
{activePage === "messages" && <MessagesPage />}
|
||||||
{/* {activePage === "map" && <MapPage />} */}
|
{activePage === "map" && <MapPage />}
|
||||||
{activePage === "extensions" && <ExtensionsPage />}
|
{activePage === "extensions" && <ExtensionsPage />}
|
||||||
{activePage === "config" && <ConfigPage />}
|
{activePage === "config" && <ConfigPage />}
|
||||||
{activePage === "channels" && <ChannelsPage />}
|
{activePage === "channels" && <ChannelsPage />}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import { Pane } from "evergreen-ui";
|
||||||
|
|
||||||
|
export const HelpDialog = (): JSX.Element => {
|
||||||
|
return <Pane></Pane>;
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
|
||||||
import { toMGRS } from "@app/core/utils/toMGRS.js";
|
import { toMGRS } from "@app/core/utils/toMGRS.js";
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ export const QRDialog = ({
|
|||||||
settings: channelsToEncode,
|
settings: channelsToEncode,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const base64 = fromByteArray(encoded);
|
const base64 = fromByteArray(encoded)
|
||||||
|
.replace(/=/g, "")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_");
|
||||||
|
|
||||||
setQRCodeURL(`https://www.meshtastic.org/e/#${base64}`);
|
setQRCodeURL(`https://www.meshtastic.org/e/#${base64}`);
|
||||||
}, [channels, selectedChannels, loraConfig]);
|
}, [channels, selectedChannels, loraConfig]);
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { FormField, SelectField, Switch, TextInputField } from "evergreen-ui";
|
||||||
|
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||||
|
|
||||||
|
import { BluetoothValidation } from "@app/validation/config/bluetooth.js";
|
||||||
|
import { Form } from "@components/form/Form";
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
export const Bluetooth = (): JSX.Element => {
|
||||||
|
const { config, connection } = useDevice();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors, isDirty },
|
||||||
|
control,
|
||||||
|
reset,
|
||||||
|
} = useForm<BluetoothValidation>({
|
||||||
|
defaultValues: config.bluetooth,
|
||||||
|
resolver: classValidatorResolver(BluetoothValidation),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reset(config.bluetooth);
|
||||||
|
}, [reset, config.bluetooth]);
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((data) => {
|
||||||
|
setLoading(true);
|
||||||
|
void connection?.setConfig(
|
||||||
|
{
|
||||||
|
payloadVariant: {
|
||||||
|
oneofKind: "bluetooth",
|
||||||
|
bluetooth: data,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
reset({ ...data });
|
||||||
|
setLoading(false);
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const pairingMode = useWatch({
|
||||||
|
control,
|
||||||
|
name: "mode",
|
||||||
|
defaultValue: Protobuf.Config_BluetoothConfig_PairingMode.RandomPin,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form loading={loading} dirty={isDirty} onSubmit={onSubmit}>
|
||||||
|
<FormField
|
||||||
|
label="Bluetooth Enabled"
|
||||||
|
description="Description"
|
||||||
|
isInvalid={!!errors.enabled?.message}
|
||||||
|
validationMessage={errors.enabled?.message}
|
||||||
|
>
|
||||||
|
<Controller
|
||||||
|
name="enabled"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, ...field } }) => (
|
||||||
|
<Switch height={24} marginLeft="auto" checked={value} {...field} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<SelectField
|
||||||
|
label="Pairing mode"
|
||||||
|
description="This is a description."
|
||||||
|
isInvalid={!!errors.mode?.message}
|
||||||
|
validationMessage={errors.mode?.message}
|
||||||
|
{...register("mode", { valueAsNumber: true })}
|
||||||
|
>
|
||||||
|
{renderOptions(Protobuf.Config_BluetoothConfig_PairingMode)}
|
||||||
|
</SelectField>
|
||||||
|
|
||||||
|
<TextInputField
|
||||||
|
display={
|
||||||
|
pairingMode !== Protobuf.Config_BluetoothConfig_PairingMode.FixedPin
|
||||||
|
? "none"
|
||||||
|
: "block"
|
||||||
|
}
|
||||||
|
label="Pin"
|
||||||
|
description="This is a description."
|
||||||
|
type="number"
|
||||||
|
isInvalid={!!errors.fixedPin?.message}
|
||||||
|
validationMessage={errors.fixedPin?.message}
|
||||||
|
{...register("fixedPin", {
|
||||||
|
valueAsNumber: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -12,7 +12,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { DeviceValidation } from "@app/validation/config/device.js";
|
import { DeviceValidation } from "@app/validation/config/device.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { DisplayValidation } from "@app/validation/config/display.js";
|
import { DisplayValidation } from "@app/validation/config/display.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { LoRaValidation } from "@app/validation/config/lora.js";
|
import { LoRaValidation } from "@app/validation/config/lora.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { PositionValidation } from "@app/validation/config/position.js";
|
import { PositionValidation } from "@app/validation/config/position.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { bitwiseDecode } from "@core/utils/bitwise";
|
import { bitwiseDecode } from "@core/utils/bitwise";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { PowerValidation } from "@app/validation/config/power.js";
|
import { PowerValidation } from "@app/validation/config/power.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { base16 } from "rfc4648";
|
|||||||
|
|
||||||
import { UserValidation } from "@app/validation/config/user.js";
|
import { UserValidation } from "@app/validation/config/user.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
@@ -103,27 +103,6 @@ export const User = (): JSX.Element => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
<TextInputField
|
|
||||||
label="Transmit Power"
|
|
||||||
description="This is a description."
|
|
||||||
hint="dBm"
|
|
||||||
type="number"
|
|
||||||
{...register("txPowerDbm", { valueAsNumber: true })}
|
|
||||||
/>
|
|
||||||
<TextInputField
|
|
||||||
label="Antenna Gain"
|
|
||||||
description="This is a description."
|
|
||||||
hint="dBi"
|
|
||||||
type="number"
|
|
||||||
{...register("antGainDbi", { valueAsNumber: true })}
|
|
||||||
/>
|
|
||||||
<TextInputField
|
|
||||||
label="Antenna Azimuth"
|
|
||||||
description="This is a description."
|
|
||||||
hint="°"
|
|
||||||
type="number"
|
|
||||||
{...register("antAzimuth", { valueAsNumber: true })}
|
|
||||||
/>
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
||||||
import { WiFiValidation } from "@app/validation/config/wifi.js";
|
import { WiFiValidation } from "@app/validation/config/wifi.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.js";
|
import { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@core/utils/selectEnumOptions.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.js";
|
import { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const ExternalNotification = (): JSX.Element => {
|
export const ExternalNotification = (): JSX.Element => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { MQTTValidation } from "@app/validation/moduleConfig/mqtt.js";
|
import { MQTTValidation } from "@app/validation/moduleConfig/mqtt.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const MQTT = (): JSX.Element => {
|
export const MQTT = (): JSX.Element => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.js";
|
import { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const RangeTest = (): JSX.Element => {
|
export const RangeTest = (): JSX.Element => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { SerialValidation } from "@app/validation/moduleConfig/serial.js";
|
import { SerialValidation } from "@app/validation/moduleConfig/serial.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const Serial = (): JSX.Element => {
|
export const Serial = (): JSX.Element => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
|
|||||||
|
|
||||||
import { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.js";
|
import { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const StoreForward = (): JSX.Element => {
|
export const StoreForward = (): JSX.Element => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
|
|
||||||
import { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.js";
|
import { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.js";
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||||
|
|
||||||
export const Telemetry = (): JSX.Element => {
|
export const Telemetry = (): JSX.Element => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
StatusIndicator,
|
StatusIndicator,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
|
||||||
export const Progress = (): JSX.Element => {
|
export const Progress = (): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { useEffect, useState } from "react";
|
|||||||
|
|
||||||
import { GeolocationIcon, Pane, PropertyIcon, SideSheet } from "evergreen-ui";
|
import { GeolocationIcon, Pane, PropertyIcon, SideSheet } from "evergreen-ui";
|
||||||
|
|
||||||
import { Node, useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
import type { Node } from "@core/stores/deviceStore.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ import { Overview } from "./tabs/nodes/Overview.js";
|
|||||||
|
|
||||||
export const PeerInfo = () => {
|
export const PeerInfo = () => {
|
||||||
const { peerInfoOpen, activePeer, setPeerInfoOpen, nodes } = useDevice();
|
const { peerInfoOpen, activePeer, setPeerInfoOpen, nodes } = useDevice();
|
||||||
const [selectedTab, setSelectedTab] = useState(0);
|
|
||||||
const [node, setNode] = useState<Node | undefined>();
|
const [node, setNode] = useState<Node | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import type { HTMLProps } from "react";
|
import type { HTMLProps } from "react";
|
||||||
|
|
||||||
import { Button, majorScale, Pane, Spinner } from "evergreen-ui";
|
import { Button, majorScale, Pane, SavedIcon, Spinner } from "evergreen-ui";
|
||||||
import { FiSave } from "react-icons/fi";
|
|
||||||
|
|
||||||
export interface FormProps extends HTMLProps<HTMLFormElement> {
|
export interface FormProps extends HTMLProps<HTMLFormElement> {
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
|
||||||
@@ -39,7 +38,7 @@ export const Form = ({
|
|||||||
type="submit"
|
type="submit"
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
disabled={!dirty}
|
disabled={!dirty}
|
||||||
iconBefore={<FiSave />}
|
iconBefore={<SavedIcon />}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -55,12 +55,12 @@ export const Header = (): JSX.Element => {
|
|||||||
width={majorScale(12)}
|
width={majorScale(12)}
|
||||||
marginRight={majorScale(22)}
|
marginRight={majorScale(22)}
|
||||||
>
|
>
|
||||||
<Link href="/">
|
<Link href=".">
|
||||||
<Pane
|
<Pane
|
||||||
is="img"
|
is="img"
|
||||||
width={100}
|
width={100}
|
||||||
height={28}
|
height={28}
|
||||||
src="/Logo_Black.svg"
|
src="Logo_Black.svg"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { FiBluetooth, FiTerminal, FiWifi } from "react-icons/fi";
|
import { FiBluetooth, FiTerminal, FiWifi } from "react-icons/fi";
|
||||||
|
|
||||||
import { toMGRS } from "@app/core/utils/toMGRS.js";
|
import { toMGRS } from "@app/core/utils/toMGRS.js";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Types } from "@meshtastic/meshtasticjs";
|
import { Types } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export const DeviceCard = (): JSX.Element => {
|
|||||||
<Heading>{myNode?.data.user?.longName}</Heading>
|
<Heading>{myNode?.data.user?.longName}</Heading>
|
||||||
<Link
|
<Link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/meshtastic/meshtastic-web/releases/"
|
href="https://github.com/meshtastic/meshtastic-device/releases/"
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
color="green"
|
color="green"
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
import { PeersDialog } from "@app/components/Dialog/PeersDialog.js";
|
import { PeersDialog } from "@app/components/Dialog/PeersDialog.js";
|
||||||
import { Page, useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
import type { Page } from "@core/stores/deviceStore.js";
|
||||||
|
|
||||||
import { DeviceCard } from "./DeviceCard.js";
|
import { DeviceCard } from "./DeviceCard.js";
|
||||||
|
|
||||||
@@ -42,7 +43,6 @@ export const Sidebar = (): JSX.Element => {
|
|||||||
name: "Map",
|
name: "Map",
|
||||||
icon: GlobeIcon,
|
icon: GlobeIcon,
|
||||||
page: "map",
|
page: "map",
|
||||||
disabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Extensions",
|
name: "Extensions",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
import type { Device } from "../stores/deviceStore.js";
|
||||||
|
|
||||||
|
export const DeviceContext = createContext<Device | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useDevice = (): Device => {
|
||||||
|
const context = useContext(DeviceContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useDevice must be used within a ConnectionProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -44,10 +44,10 @@ export interface Device {
|
|||||||
activePage: Page;
|
activePage: Page;
|
||||||
peerInfoOpen: boolean;
|
peerInfoOpen: boolean;
|
||||||
activePeer: number;
|
activePeer: number;
|
||||||
|
waypoints: Protobuf.Location[];
|
||||||
|
|
||||||
setReady(ready: boolean): void;
|
setReady(ready: boolean): void;
|
||||||
setStatus: (status: Types.DeviceStatusEnum) => void;
|
setStatus: (status: Types.DeviceStatusEnum) => void;
|
||||||
addChannel: (channel: Channel) => void;
|
|
||||||
setConfig: (config: Protobuf.Config) => void;
|
setConfig: (config: Protobuf.Config) => void;
|
||||||
setModuleConfig: (config: Protobuf.ModuleConfig) => void;
|
setModuleConfig: (config: Protobuf.ModuleConfig) => void;
|
||||||
setHardware: (hardware: Protobuf.MyNodeInfo) => void;
|
setHardware: (hardware: Protobuf.MyNodeInfo) => void;
|
||||||
@@ -55,6 +55,8 @@ export interface Device {
|
|||||||
setActivePage: (page: Page) => void;
|
setActivePage: (page: Page) => void;
|
||||||
setPeerInfoOpen: (open: boolean) => void;
|
setPeerInfoOpen: (open: boolean) => void;
|
||||||
setActivePeer: (peer: number) => void;
|
setActivePeer: (peer: number) => void;
|
||||||
|
addChannel: (channel: Channel) => void;
|
||||||
|
addWaypoint: (waypoint: Protobuf.Location) => void;
|
||||||
addNodeInfo: (nodeInfo: Types.NodeInfoPacket) => void;
|
addNodeInfo: (nodeInfo: Types.NodeInfoPacket) => void;
|
||||||
addUser: (user: Types.UserPacket) => void;
|
addUser: (user: Types.UserPacket) => void;
|
||||||
addPosition: (position: Types.PositionPacket) => void;
|
addPosition: (position: Types.PositionPacket) => void;
|
||||||
@@ -92,6 +94,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
|
|||||||
activePage: "messages",
|
activePage: "messages",
|
||||||
peerInfoOpen: false,
|
peerInfoOpen: false,
|
||||||
activePeer: 0,
|
activePeer: 0,
|
||||||
|
waypoints: [],
|
||||||
|
|
||||||
setReady: (ready: boolean) => {
|
setReady: (ready: boolean) => {
|
||||||
set(
|
set(
|
||||||
@@ -113,25 +116,6 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
addChannel: (channel: Channel) => {
|
|
||||||
set(
|
|
||||||
produce<DeviceState>((draft) => {
|
|
||||||
const device = draft.devices.get(id);
|
|
||||||
if (device) {
|
|
||||||
const channelIndex = device.channels.findIndex(
|
|
||||||
(c) => c.config.index === channel.config.index
|
|
||||||
);
|
|
||||||
if (channelIndex !== -1) {
|
|
||||||
const messages = device.channels[channelIndex].messages;
|
|
||||||
device.channels[channelIndex] = channel;
|
|
||||||
device.channels[channelIndex].messages = messages;
|
|
||||||
} else {
|
|
||||||
device.channels.push(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
setConfig: (config: Protobuf.Config) => {
|
setConfig: (config: Protobuf.Config) => {
|
||||||
set(
|
set(
|
||||||
produce<DeviceState>((draft) => {
|
produce<DeviceState>((draft) => {
|
||||||
@@ -257,6 +241,43 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
addChannel: (channel: Channel) => {
|
||||||
|
set(
|
||||||
|
produce<DeviceState>((draft) => {
|
||||||
|
const device = draft.devices.get(id);
|
||||||
|
if (device) {
|
||||||
|
const channelIndex = device.channels.findIndex(
|
||||||
|
(c) => c.config.index === channel.config.index
|
||||||
|
);
|
||||||
|
if (channelIndex !== -1) {
|
||||||
|
const messages = device.channels[channelIndex].messages;
|
||||||
|
device.channels[channelIndex] = channel;
|
||||||
|
device.channels[channelIndex].messages = messages;
|
||||||
|
} else {
|
||||||
|
device.channels.push(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
addWaypoint: (waypoint: Protobuf.Location) => {
|
||||||
|
set(
|
||||||
|
produce<DeviceState>((draft) => {
|
||||||
|
const device = draft.devices.get(id);
|
||||||
|
if (device) {
|
||||||
|
const waypointIndex = device.waypoints.findIndex(
|
||||||
|
(wp) => wp.id === waypoint.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (waypointIndex !== -1) {
|
||||||
|
device.waypoints[waypointIndex] = waypoint;
|
||||||
|
} else {
|
||||||
|
device.waypoints.push(waypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
addNodeInfo: (nodeInfo) => {
|
addNodeInfo: (nodeInfo) => {
|
||||||
set(
|
set(
|
||||||
produce<DeviceState>((draft) => {
|
produce<DeviceState>((draft) => {
|
||||||
|
|||||||
@@ -71,5 +71,8 @@ export const subscribeAll = (device: Device, connection: IConnection) => {
|
|||||||
? new Date(messagePacket.packet.rxTime * 1000)
|
? new Date(messagePacket.packet.rxTime * 1000)
|
||||||
: new Date(),
|
: new Date(),
|
||||||
});
|
});
|
||||||
|
if (messagePacket.location) {
|
||||||
|
device.addWaypoint(messagePacket.location);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
export const fetcher = async <JSON>(
|
|
||||||
input: RequestInfo,
|
|
||||||
init?: RequestInit
|
|
||||||
): Promise<JSON> => {
|
|
||||||
const res = await fetch(input, init);
|
|
||||||
return res.json() as Promise<JSON>;
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import "modern-css-reset/dist/reset.min.css";
|
import "modern-css-reset/dist/reset.min.css";
|
||||||
|
import "maplibre-gl/dist/maplibre-gl.css";
|
||||||
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { Form } from "@components/form/Form";
|
import { Form } from "@components/form/Form";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
export interface SettingsPanelProps {
|
export interface SettingsPanelProps {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { IoQrCodeOutline } from "react-icons/io5";
|
|||||||
|
|
||||||
import { QRDialog } from "@app/components/Dialog/QRDialog.js";
|
import { QRDialog } from "@app/components/Dialog/QRDialog.js";
|
||||||
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
|
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import { Channel } from "./Channel.js";
|
import { Channel } from "./Channel.js";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
|||||||
|
|
||||||
import { Pane, Tab, Tablist } from "evergreen-ui";
|
import { Pane, Tab, Tablist } from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { Bluetooth } from "@app/components/PageComponents/Config/Bluetooth.js";
|
||||||
import { Device } from "@components/PageComponents/Config/Device.js";
|
import { Device } from "@components/PageComponents/Config/Device.js";
|
||||||
import { Display } from "@components/PageComponents/Config/Display.js";
|
import { Display } from "@components/PageComponents/Config/Display.js";
|
||||||
import { LoRa } from "@components/PageComponents/Config/LoRa.js";
|
import { LoRa } from "@components/PageComponents/Config/LoRa.js";
|
||||||
@@ -11,6 +11,7 @@ import { Position } from "@components/PageComponents/Config/Position.js";
|
|||||||
import { Power } from "@components/PageComponents/Config/Power.js";
|
import { Power } from "@components/PageComponents/Config/Power.js";
|
||||||
import { User } from "@components/PageComponents/Config/User.js";
|
import { User } from "@components/PageComponents/Config/User.js";
|
||||||
import { WiFi } from "@components/PageComponents/Config/WiFi.js";
|
import { WiFi } from "@components/PageComponents/Config/WiFi.js";
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
|
||||||
export const DeviceConfig = (): JSX.Element => {
|
export const DeviceConfig = (): JSX.Element => {
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
@@ -46,8 +47,10 @@ export const DeviceConfig = (): JSX.Element => {
|
|||||||
label: "LoRa",
|
label: "LoRa",
|
||||||
element: LoRa,
|
element: LoRa,
|
||||||
},
|
},
|
||||||
// Channels
|
{
|
||||||
// Interface
|
label: "Bluetooth",
|
||||||
|
element: Bluetooth,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ export const ModuleConfig = (): JSX.Element => {
|
|||||||
label: "Canned Message",
|
label: "Canned Message",
|
||||||
element: CannedMessage,
|
element: CannedMessage,
|
||||||
},
|
},
|
||||||
// Channels
|
|
||||||
// Interface
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type React from "react";
|
|||||||
|
|
||||||
import { Pane } from "evergreen-ui";
|
import { Pane } from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
|
||||||
export const Environment = (): JSX.Element => {
|
export const Environment = (): JSX.Element => {
|
||||||
const { nodes } = useDevice();
|
const { nodes } = useDevice();
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Pane } from "evergreen-ui";
|
import { Pane } from "evergreen-ui";
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
import { fetcher } from "@core/utils/fetcher";
|
|
||||||
|
|
||||||
export interface File {
|
export interface File {
|
||||||
nameModified: string;
|
nameModified: string;
|
||||||
@@ -24,10 +22,15 @@ export interface Files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const FileBrowser = (): JSX.Element => {
|
export const FileBrowser = (): JSX.Element => {
|
||||||
const { data } = useSWR<Files>(
|
const [data, setData] = useState<Files>();
|
||||||
"http://meshtastic.local/json/fs/browse/static",
|
|
||||||
fetcher
|
useEffect(() => {
|
||||||
);
|
void fetch("http://meshtastic.local/json/fs/browse/static").then(
|
||||||
|
async (res) => {
|
||||||
|
setData((await res.json()) as Files);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pane>
|
<Pane>
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import type React from "react";
|
|||||||
|
|
||||||
import { DocumentIcon, GanttChartIcon, RainIcon } from "evergreen-ui";
|
import { DocumentIcon, GanttChartIcon, RainIcon } from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
|
||||||
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
|
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { FileBrowser } from "@pages/Extensions/FileBrowser";
|
import { FileBrowser } from "@pages/Extensions/FileBrowser";
|
||||||
|
|
||||||
import { Environment } from "./Environment.js";
|
import { Environment } from "./Environment.js";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type React from "react";
|
|||||||
import { Pane } from "evergreen-ui";
|
import { Pane } from "evergreen-ui";
|
||||||
import JSONPretty from "react-json-pretty";
|
import JSONPretty from "react-json-pretty";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
|
||||||
export const InfoPage = (): JSX.Element => {
|
export const InfoPage = (): JSX.Element => {
|
||||||
const { hardware, nodes } = useDevice();
|
const { hardware, nodes } = useDevice();
|
||||||
|
|||||||
+103
-128
@@ -1,137 +1,112 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useEffect, useMemo, useRef } from "react";
|
|
||||||
|
|
||||||
import { Pane } from "evergreen-ui";
|
import {
|
||||||
|
Heading,
|
||||||
|
IconButton,
|
||||||
|
LocateIcon,
|
||||||
|
majorScale,
|
||||||
|
MapMarkerIcon,
|
||||||
|
Pane,
|
||||||
|
Text,
|
||||||
|
} from "evergreen-ui";
|
||||||
|
import maplibregl from "maplibre-gl";
|
||||||
|
import { Map, Marker, useMap } from "react-map-gl";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import Point from "@arcgis/core/geometry/Point";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import Graphic from "@arcgis/core/Graphic";
|
|
||||||
import FeatureLayer from "@arcgis/core/layers/FeatureLayer";
|
|
||||||
import LabelClass from "@arcgis/core/layers/support/LabelClass";
|
|
||||||
import Map from "@arcgis/core/Map";
|
|
||||||
import LineCallout3D from "@arcgis/core/symbols/callouts/LineCallout3D";
|
|
||||||
import LabelSymbol3D from "@arcgis/core/symbols/LabelSymbol3D";
|
|
||||||
import TextSymbol3DLayer from "@arcgis/core/symbols/TextSymbol3DLayer";
|
|
||||||
import SceneView from "@arcgis/core/views/SceneView";
|
|
||||||
|
|
||||||
export const MapPage = (): JSX.Element => {
|
export const MapPage = (): JSX.Element => {
|
||||||
const { nodes } = useDevice();
|
const { nodes, waypoints } = useDevice();
|
||||||
|
const { current: map } = useMap();
|
||||||
|
|
||||||
const nodesWithPosition = nodes.filter((node) => node.data.position);
|
return (
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
<Pane
|
||||||
|
margin={majorScale(3)}
|
||||||
|
borderRadius={majorScale(1)}
|
||||||
|
elevation={1}
|
||||||
|
display="flex"
|
||||||
|
flexGrow={1}
|
||||||
|
flexDirection="column"
|
||||||
|
gap={majorScale(2)}
|
||||||
|
overflow="hidden"
|
||||||
|
position="relative"
|
||||||
|
>
|
||||||
|
<Pane
|
||||||
|
position="absolute"
|
||||||
|
zIndex={10}
|
||||||
|
right={0}
|
||||||
|
top={0}
|
||||||
|
borderRadius={majorScale(1)}
|
||||||
|
padding={majorScale(1)}
|
||||||
|
margin={majorScale(1)}
|
||||||
|
background="tint1"
|
||||||
|
width={majorScale(28)}
|
||||||
|
elevation={1}
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
<Pane padding={majorScale(1)} background="tint2">
|
||||||
|
<Heading>Title</Heading>
|
||||||
|
</Pane>
|
||||||
|
<Pane display="flex" flexDirection="column" gap={majorScale(1)}>
|
||||||
|
{nodes.map((n) => (
|
||||||
|
<Pane key={n.data.num} display="flex" gap={majorScale(1)}>
|
||||||
|
<Hashicon value={n.data.num.toString()} size={24} />
|
||||||
|
<Text>{n.data.user?.longName}</Text>
|
||||||
|
<IconButton
|
||||||
|
icon={LocateIcon}
|
||||||
|
marginLeft="auto"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("clicked");
|
||||||
|
console.log(map);
|
||||||
|
|
||||||
useEffect(() => {
|
map?.flyTo({
|
||||||
console.log(nodesWithPosition);
|
center: [
|
||||||
}, [nodesWithPosition]);
|
n.data.position?.latitudeI / 1e7,
|
||||||
|
n.data.position?.longitudeI / 1e7,
|
||||||
const labelClass = useMemo(
|
],
|
||||||
() =>
|
zoom: 10,
|
||||||
new LabelClass({
|
});
|
||||||
labelExpressionInfo: {
|
}}
|
||||||
expression: "$feature.name",
|
/>
|
||||||
},
|
</Pane>
|
||||||
symbol: new LabelSymbol3D({
|
))}
|
||||||
symbolLayers: [
|
</Pane>
|
||||||
new TextSymbol3DLayer({
|
</Pane>
|
||||||
text: "{name}",
|
<Map
|
||||||
material: {
|
mapStyle="https://raw.githubusercontent.com/hc-oss/maplibre-gl-styles/master/styles/osm-mapnik/v8/default.json"
|
||||||
color: "black",
|
mapLib={maplibregl}
|
||||||
},
|
attributionControl={false}
|
||||||
halo: {
|
>
|
||||||
color: [255, 255, 255, 0.7],
|
{waypoints.map((wp) => (
|
||||||
size: 2,
|
<Marker
|
||||||
},
|
key={wp.id}
|
||||||
font: {
|
longitude={wp.longitudeI / 1e7}
|
||||||
size: 12,
|
latitude={wp.latitudeI / 1e7}
|
||||||
weight: "bold",
|
anchor="bottom"
|
||||||
},
|
>
|
||||||
size: 10,
|
<Pane>
|
||||||
}),
|
<MapMarkerIcon />
|
||||||
],
|
</Pane>
|
||||||
verticalOffset: {
|
</Marker>
|
||||||
screenLength: 150,
|
))}
|
||||||
maxWorldLength: 2000,
|
{nodes
|
||||||
minWorldLength: 30,
|
.filter((n) => n.data.position?.latitudeI)
|
||||||
},
|
.map((n) => {
|
||||||
callout: new LineCallout3D({
|
if (n.data.position?.latitudeI) {
|
||||||
size: 0.5,
|
return (
|
||||||
color: [0, 0, 0],
|
<Marker
|
||||||
border: {
|
key={n.data.num}
|
||||||
color: [255, 255, 255],
|
longitude={n.data.position.longitudeI / 1e7}
|
||||||
},
|
latitude={n.data.position.latitudeI / 1e7}
|
||||||
}),
|
anchor="bottom"
|
||||||
}),
|
>
|
||||||
}),
|
<Hashicon value={n.data.num.toString()} size={32} />
|
||||||
[]
|
</Marker>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Map>
|
||||||
|
</Pane>
|
||||||
);
|
);
|
||||||
|
|
||||||
const points: Graphic[] = nodesWithPosition.map(
|
|
||||||
(node, index) =>
|
|
||||||
node.data.position
|
|
||||||
? new Graphic({
|
|
||||||
geometry: new Point({
|
|
||||||
latitude: node.data.position.latitudeI / 1e7,
|
|
||||||
longitude: node.data.position.longitudeI / 1e7,
|
|
||||||
}),
|
|
||||||
attributes: {
|
|
||||||
ObjectID: index,
|
|
||||||
name: node.data.user?.longName,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
: new Graphic() //should be undefined/removed from array
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current) {
|
|
||||||
const layer = new FeatureLayer({
|
|
||||||
labelsVisible: true,
|
|
||||||
labelingInfo: [labelClass],
|
|
||||||
source: points,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "ObjectID",
|
|
||||||
alias: "ObjectID",
|
|
||||||
type: "oid",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "name",
|
|
||||||
alias: "Name",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const map = new Map({
|
|
||||||
basemap: "satellite",
|
|
||||||
ground: "world-elevation",
|
|
||||||
layers: [layer],
|
|
||||||
});
|
|
||||||
|
|
||||||
const scene = new SceneView({
|
|
||||||
container: ref.current,
|
|
||||||
map: map,
|
|
||||||
camera: {
|
|
||||||
position: nodesWithPosition[0]
|
|
||||||
? {
|
|
||||||
x: nodesWithPosition[0].data.position?.longitudeI ?? 0 / 1e7,
|
|
||||||
y: nodesWithPosition[0].data.position?.latitudeI ?? 0 / 1e7,
|
|
||||||
z: nodesWithPosition[0].data.position?.altitude ?? 0 / 1e7,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
y: -35.59, //Longitude
|
|
||||||
x: 148, //Latitude
|
|
||||||
z: 200, //Meters
|
|
||||||
},
|
|
||||||
tilt: 75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
scene.on("click", (event) => {
|
|
||||||
void scene.hitTest(event).then((point) => {
|
|
||||||
console.log(point);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [labelClass, points]);
|
|
||||||
|
|
||||||
return <Pane width="100%" height="100%" ref={ref} />;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import type { Channel } from "@core/stores/deviceStore.js";
|
import type { Channel } from "@core/stores/deviceStore.js";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
|
||||||
|
|
||||||
import { Message } from "./Message.js";
|
import { Message } from "./Message.js";
|
||||||
import { NewLocationMessage } from "./NewLocationMessage.js";
|
import { NewLocationMessage } from "./NewLocationMessage.js";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import type { Protobuf, Types } from "@meshtastic/meshtasticjs";
|
import type { Protobuf, Types } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
TextInputField,
|
TextInputField,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
|
||||||
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
||||||
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
enum LocationType {
|
enum LocationType {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
TabbedContent,
|
TabbedContent,
|
||||||
TabType,
|
TabType,
|
||||||
} from "@components/layout/page/TabbedContent.js";
|
} from "@components/layout/page/TabbedContent.js";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import { ChannelChat } from "./ChannelChat.js";
|
import { ChannelChat } from "./ChannelChat.js";
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { IsBoolean, IsEnum, IsInt } from "class-validator";
|
||||||
|
|
||||||
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
export class BluetoothValidation implements Protobuf.Config_BluetoothConfig {
|
||||||
|
@IsBoolean()
|
||||||
|
enabled: boolean;
|
||||||
|
|
||||||
|
@IsEnum(Protobuf.Config_BluetoothConfig_PairingMode)
|
||||||
|
mode: Protobuf.Config_BluetoothConfig_PairingMode;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
fixedPin: number;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsBoolean, IsInt, Length } from "class-validator";
|
import { IsBoolean, Length } from "class-validator";
|
||||||
|
|
||||||
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
@@ -16,13 +16,4 @@ export class UserValidation
|
|||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
isLicensed: boolean;
|
isLicensed: boolean;
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
txPowerDbm: number;
|
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
antGainDbi: number;
|
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
antAzimuth: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+16
-29
@@ -4,71 +4,58 @@
|
|||||||
///<reference types="chrome"/>
|
///<reference types="chrome"/>
|
||||||
|
|
||||||
/* CSS MODULES */
|
/* CSS MODULES */
|
||||||
declare module '*.module.css' {
|
declare module "*.module.css" {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
declare module '*.module.scss' {
|
declare module "*.module.scss" {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
declare module '*.module.sass' {
|
declare module "*.module.sass" {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
declare module '*.module.less' {
|
declare module "*.module.less" {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
declare module '*.module.styl' {
|
declare module "*.module.styl" {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSS */
|
/* CSS */
|
||||||
declare module '*.css';
|
declare module "*.css";
|
||||||
declare module '*.scss';
|
declare module "*.scss";
|
||||||
declare module '*.sass';
|
declare module "*.sass";
|
||||||
declare module '*.less';
|
declare module "*.less";
|
||||||
declare module '*.styl';
|
declare module "*.styl";
|
||||||
|
|
||||||
/* IMAGES */
|
/* IMAGES */
|
||||||
declare module '*.svg' {
|
declare module "*.svg" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
declare module '*.bmp' {
|
declare module "*.bmp" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
declare module '*.gif' {
|
declare module "*.gif" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
declare module '*.jpg' {
|
declare module "*.jpg" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
declare module '*.jpeg' {
|
declare module "*.jpeg" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
declare module '*.png' {
|
declare module "*.png" {
|
||||||
const ref: string;
|
const ref: string;
|
||||||
export default ref;
|
export default ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CUSTOM: ADD YOUR OWN HERE */
|
/* CUSTOM: ADD YOUR OWN HERE */
|
||||||
|
|
||||||
type Primitive = string | number | boolean | symbol | undefined | null;
|
|
||||||
type DeepOmitHelper<T, K extends keyof T> = {
|
|
||||||
[P in K]: T[P] extends infer TP //extra level of indirection needed to trigger homomorhic behavior // distribute over unions
|
|
||||||
? TP extends Primitive
|
|
||||||
? TP // leave primitives and functions alone
|
|
||||||
: DeepOmit<TP, K>
|
|
||||||
: never;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DeepOmit<T, K> = T extends Primitive
|
|
||||||
? T
|
|
||||||
: DeepOmitHelper<T, Exclude<keyof T, K>>;
|
|
||||||
|
|||||||
+16
-65
@@ -1,18 +1,17 @@
|
|||||||
import { execSync } from 'child_process';
|
import { execSync } from "child_process";
|
||||||
import { resolve } from 'path';
|
import { resolve } from "path";
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from "vite";
|
||||||
import importToCDN from 'vite-plugin-cdn-import';
|
import EnvironmentPlugin from "vite-plugin-environment";
|
||||||
import EnvironmentPlugin from 'vite-plugin-environment';
|
|
||||||
|
|
||||||
import react from '@vitejs/plugin-react';
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
let hash = '';
|
let hash = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hash = execSync('git rev-parse --short HEAD').toString().trim();
|
hash = execSync("git rev-parse --short HEAD").toString().trim();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
hash = 'DEVELOPMENT';
|
hash = "DEVELOPMENT";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -21,69 +20,21 @@ export default defineConfig({
|
|||||||
EnvironmentPlugin({
|
EnvironmentPlugin({
|
||||||
COMMIT_HASH: hash,
|
COMMIT_HASH: hash,
|
||||||
}),
|
}),
|
||||||
importToCDN({
|
|
||||||
modules: [
|
|
||||||
// {
|
|
||||||
// name: 'mapbox-gl',
|
|
||||||
// var: 'mapboxgl',
|
|
||||||
// path: `dist/mapbox-gl.js`,
|
|
||||||
// },
|
|
||||||
// autoComplete('@arcgis/core'),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
// VitePWA({
|
|
||||||
// mode: 'production',
|
|
||||||
|
|
||||||
// includeAssets: [
|
|
||||||
// 'favicon.svg',
|
|
||||||
// 'favicon.ico',
|
|
||||||
// 'robots.txt',
|
|
||||||
// 'touch-icon.png',
|
|
||||||
// ],
|
|
||||||
// manifest: {
|
|
||||||
// name: 'Meshtastic Web',
|
|
||||||
// short_name: 'Meshtastic',
|
|
||||||
// description: 'Meshtastic Web App',
|
|
||||||
// theme_color: '#67ea94',
|
|
||||||
// icons: [
|
|
||||||
// {
|
|
||||||
// src: 'android-192.png',
|
|
||||||
// sizes: '192x192',
|
|
||||||
// type: 'image/png',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// src: 'android-512.png',
|
|
||||||
// sizes: '512x512',
|
|
||||||
// type: 'image/png',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// src: 'android-512.png',
|
|
||||||
// sizes: '512x512',
|
|
||||||
// type: 'image/png',
|
|
||||||
// purpose: 'any maskable',
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// workbox: {
|
|
||||||
// sourcemap: true,
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
target: 'esnext',
|
target: "esnext",
|
||||||
assetsDir: '',
|
assetsDir: "",
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
plugins: [visualizer()],
|
plugins: [visualizer()],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@app': resolve(__dirname, './src'),
|
"@app": resolve(__dirname, "./src"),
|
||||||
'@pages': resolve(__dirname, './src/pages'),
|
"@pages": resolve(__dirname, "./src/pages"),
|
||||||
'@components': resolve(__dirname, './src/components'),
|
"@components": resolve(__dirname, "./src/components"),
|
||||||
'@core': resolve(__dirname, './src/core'),
|
"@core": resolve(__dirname, "./src/core"),
|
||||||
'@layouts': resolve(__dirname, './src/layouts'),
|
"@layouts": resolve(__dirname, "./src/layouts"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user