diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 71377d24..cd73ce31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,6 +1,9 @@
name: CI
-on: push
+on:
+ push:
+ branches:
+ - master
permissions:
contents: write
@@ -21,42 +24,3 @@ jobs:
- name: Build Package
run: pnpm build
-
- - name: Package Output
- run: pnpm package
-
- - name: Upload Artifact
- uses: "marvinpinto/action-automatic-releases@latest"
- with:
- repo_token: "${{ secrets.GITHUB_TOKEN }}"
- automatic_release_tag: "latest"
- prerelease: false
- files: |
- ./dist/build.tar
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v3
-
- - name: Buildah Build
- id: build-container
- uses: redhat-actions/buildah-build@v2
- with:
- containerfiles: |
- ./Containerfile
- image: ${{github.event.repository.full_name}}
- tags: latest ${{ github.sha }}
- oci: true
- platforms: linux/amd64, linux/arm64
-
- - name: Push To Registry
- id: push-to-registry
- uses: redhat-actions/push-to-registry@v2
- with:
- image: ${{ steps.build-container.outputs.image }}
- tags: ${{ steps.build-container.outputs.tags }}
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Print image url
- run: echo "Image pushed to ${{ steps.push-to-registry.outputs.registry-paths }}"
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 9828a7ff..870ca6fe 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -19,3 +19,12 @@ jobs:
- name: Build Package
run: pnpm build
+
+ - name: Compress build
+ run: pnpm package
+
+ - name: Archive compressed build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build
+ path: dist/build.tar
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..2ac7ae19
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,55 @@
+name: 'Release'
+
+on:
+ release:
+ types: [released]
+
+permissions:
+ contents: write
+ packages: write
+
+jobs:
+ build-and-package:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: latest
+
+ - name: Install Dependencies
+ run: pnpm install
+
+ - name: Build Package
+ run: pnpm build
+
+ - name: Package Output
+ run: pnpm package
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Buildah Build
+ id: build-container
+ uses: redhat-actions/buildah-build@v2
+ with:
+ containerfiles: |
+ ./Containerfile
+ image: ${{github.event.repository.full_name}}
+ tags: latest ${{ github.sha }}
+ oci: true
+ platforms: linux/amd64, linux/arm64
+
+ - name: Push To Registry
+ id: push-to-registry
+ uses: redhat-actions/push-to-registry@v2
+ with:
+ image: ${{ steps.build-container.outputs.image }}
+ tags: ${{ steps.build-container.outputs.tags }}
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Print image url
+ run: echo "Image pushed to ${{ steps.push-to-registry.outputs.registry-paths }}"
diff --git a/src/App.tsx b/src/App.tsx
index 1a014eb5..48a806f4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,6 +5,7 @@ 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";
@@ -40,7 +41,11 @@ export const App = (): JSX.Element => {
) : (
-
+ <>
+
+
+
+ >
)}
diff --git a/src/components/PageComponents/Messages/ChannelChat.tsx b/src/components/PageComponents/Messages/ChannelChat.tsx
index 81ed007f..7507de97 100644
--- a/src/components/PageComponents/Messages/ChannelChat.tsx
+++ b/src/components/PageComponents/Messages/ChannelChat.tsx
@@ -68,7 +68,7 @@ export const ChannelChat = ({
)}
-
diff --git a/src/components/PageComponents/ModuleConfig/Telemetry.tsx b/src/components/PageComponents/ModuleConfig/Telemetry.tsx
index 6fb5ed4d..5af2f2b5 100644
--- a/src/components/PageComponents/ModuleConfig/Telemetry.tsx
+++ b/src/components/PageComponents/ModuleConfig/Telemetry.tsx
@@ -87,7 +87,7 @@ export const Telemetry = (): JSX.Element => {
description: "How often to send Power data over the mesh",
},
{
- type: "text",
+ type: "toggle",
name: "powerScreenEnabled",
label: "Power Screen Enabled",
description: "Enable the Power Telemetry Screen",
diff --git a/src/components/PageLayout.tsx b/src/components/PageLayout.tsx
index 274eef05..1a2e4dd7 100644
--- a/src/components/PageLayout.tsx
+++ b/src/components/PageLayout.tsx
@@ -1,5 +1,6 @@
import { cn } from "@app/core/utils/cn.js";
import { AlignLeftIcon, type LucideIcon } from "lucide-react";
+import Footer from "./UI/Footer";
export interface PageLayoutProps {
label: string;
@@ -18,40 +19,43 @@ export const PageLayout = ({
children,
}: PageLayoutProps): JSX.Element => {
return (
-
-
-
-
-
-
{label}
-
- {actions?.map((action, index) => (
-
- ))}
+ <>
+
+
+
+
+
+
{label}
+
+ {actions?.map((action, index) => (
+
+ ))}
+
+
+ {children}
+
+
-
- {children}
-
-
+ >
);
};
diff --git a/src/components/UI/Footer.tsx b/src/components/UI/Footer.tsx
new file mode 100644
index 00000000..d0e082e7
--- /dev/null
+++ b/src/components/UI/Footer.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+
+export interface FooterProps extends React.HTMLAttributes
{}
+
+const Footer = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+
+export default Footer;
diff --git a/src/components/UI/Tabs.tsx b/src/components/UI/Tabs.tsx
index 2d9b3e69..f8c83e6d 100644
--- a/src/components/UI/Tabs.tsx
+++ b/src/components/UI/Tabs.tsx
@@ -12,7 +12,7 @@ const TabsList = React.forwardRef<
{
const devices = useMemo(() => getDevices(), [getDevices]);
return (
-
-
-
-
Connected Devices
-
Manage, connect and disconnect devices
+ <>
+
+
+
+
Connected Devices
+ Manage, connect and disconnect devices
+
-
-
+
-
- {devices.length ? (
-
- {devices.map((device) => {
- return (
- -
-
-
-
- {device.nodes.get(device.hardware.myNodeNum)?.user
- ?.longName ?? "UNK"}
-
-
- {device.connection?.connType === "ble" && (
- <>
-
- BLE
- >
- )}
- {device.connection?.connType === "serial" && (
- <>
-
- Serial
- >
- )}
- {device.connection?.connType === "http" && (
- <>
-
- Network
- >
- )}
+
+ {devices.length ? (
+
+ {devices.map((device) => {
+ return (
+ -
+
+
+
+ {device.nodes.get(device.hardware.myNodeNum)?.user
+ ?.longName ?? "UNK"}
+
+
+ {device.connection?.connType === "ble" && (
+ <>
+
+ BLE
+ >
+ )}
+ {device.connection?.connType === "serial" && (
+ <>
+
+ Serial
+ >
+ )}
+ {device.connection?.connType === "http" && (
+ <>
+
+ Network
+ >
+ )}
+
-
-
-
-
- {device.nodes.size === 0 ? 0 : device.nodes.size - 1}
+
+
+
+ {device.nodes.size === 0 ? 0 : device.nodes.size - 1}
+
-
-
- );
- })}
-
- ) : (
-
-
-
No Devices
-
Connect atleast one device to get started
-
-
- )}
+
+ );
+ })}
+
+ ) : (
+
+
+
No Devices
+
Connect atleast one device to get started
+
+
+ )}
+
-
+ >
);
};
diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx
index 4afe1e62..c25f24a7 100644
--- a/src/pages/Messages.tsx
+++ b/src/pages/Messages.tsx
@@ -67,60 +67,62 @@ export const MessagesPage = (): JSX.Element => {
))}
-
+
+
+ toast({
+ title: "Traceroute sent.",
+ }),
+ );
+ },
},
- },
- ]
- : []
- }
- >
- {allChannels.map(
- (channel) =>
- activeChat === channel.index && (
-
- ),
- )}
- {filteredNodes.map(
- (node) =>
- activeChat === node.num && (
-
- ),
- )}
-
+ ]
+ : []
+ }
+ >
+ {allChannels.map(
+ (channel) =>
+ activeChat === channel.index && (
+
+ ),
+ )}
+ {filteredNodes.map(
+ (node) =>
+ activeChat === node.num && (
+
+ ),
+ )}
+
+
>
);
};
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 73ba6e46..c7682f59 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -1,3 +1,4 @@
+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";
@@ -27,73 +28,76 @@ export const NodesPage = (): JSX.Element => {
return (
<>
-
-
[
- ,
-
- {node.user?.longName ??
- (node.user?.macaddr
- ? `Meshtastic ${base16
- .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
- .toLowerCase()}`
- : `UNK: ${node.num}`)}
-
,
+
+
+
[
+ ,
+
+ {node.user?.longName ??
+ (node.user?.macaddr
+ ? `Meshtastic ${base16
+ .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
+ .toLowerCase()}`
+ : `UNK: ${node.num}`)}
+
,
-
- {Protobuf.Mesh.HardwareModel[node.user?.hwModel ?? 0]}
- ,
-
- {base16
- .stringify(node.user?.macaddr ?? [])
- .match(/.{1,2}/g)
- ?.join(":") ?? "UNK"}
- ,
-
- {node.lastHeard === 0 ? (
- Never
- ) : (
-
- )}
- ,
-
- {node.snr}db/
- {Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
- {(node.snr + 10) * 5}raw
- ,
-
- {node.lastHeard !== 0
- ? node.viaMqtt === false && node.hopsAway === 0
- ? "Direct"
- : `${node.hopsAway.toString()} ${
- node.hopsAway > 1 ? "hops" : "hop"
- } away`
- : "-"}
- {node.viaMqtt === true ? ", via MQTT" : ""}
- ,
- ,
- ])}
- />
+
+ {Protobuf.Mesh.HardwareModel[node.user?.hwModel ?? 0]}
+ ,
+
+ {base16
+ .stringify(node.user?.macaddr ?? [])
+ .match(/.{1,2}/g)
+ ?.join(":") ?? "UNK"}
+ ,
+
+ {node.lastHeard === 0 ? (
+ Never
+ ) : (
+
+ )}
+ ,
+
+ {node.snr}db/
+ {Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
+ {(node.snr + 10) * 5}raw
+ ,
+
+ {node.lastHeard !== 0
+ ? node.viaMqtt === false && node.hopsAway === 0
+ ? "Direct"
+ : `${node.hopsAway.toString()} ${
+ node.hopsAway > 1 ? "hops" : "hop"
+ } away`
+ : "-"}
+ {node.viaMqtt === true ? ", via MQTT" : ""}
+ ,
+ ,
+ ])}
+ />
+
+
>
);