diff --git a/Dockerfile b/Dockerfile
index f22f31a..29ce319 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,13 +1,15 @@
# Stage 1: Build frontend
FROM node:20-slim AS frontend-builder
+ARG COMMIT_HASH=unknown
+
WORKDIR /build
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
-RUN npm run build
+RUN VITE_COMMIT_HASH=${COMMIT_HASH} npm run build
# Stage 2: Python runtime
diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md
index 45896f9..52854e1 100644
--- a/frontend/AGENTS.md
+++ b/frontend/AGENTS.md
@@ -80,7 +80,8 @@ frontend/src/
│ │ ├── SettingsMqttSection.tsx # MQTT broker config, TLS, publish toggles
│ │ ├── SettingsDatabaseSection.tsx # DB size, cleanup, auto-decrypt, local label
│ │ ├── SettingsBotSection.tsx # Bot list, code editor, add/delete/reset
-│ │ └── SettingsStatisticsSection.tsx # Read-only mesh network stats
+│ │ ├── SettingsStatisticsSection.tsx # Read-only mesh network stats
+│ │ └── SettingsAboutSection.tsx # Version, author, license, links
│ ├── repeater/
│ │ ├── repeaterPaneShared.tsx # Shared: RepeaterPane, KvRow, format helpers
│ │ ├── RepeaterTelemetryPane.tsx # Battery, airtime, packet counts
diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx
index 989fbb3..3059946 100644
--- a/frontend/src/components/SettingsModal.tsx
+++ b/frontend/src/components/SettingsModal.tsx
@@ -16,6 +16,7 @@ import { SettingsMqttSection } from './settings/SettingsMqttSection';
import { SettingsDatabaseSection } from './settings/SettingsDatabaseSection';
import { SettingsBotSection } from './settings/SettingsBotSection';
import { SettingsStatisticsSection } from './settings/SettingsStatisticsSection';
+import { SettingsAboutSection } from './settings/SettingsAboutSection';
interface SettingsModalBaseProps {
open: boolean;
@@ -77,6 +78,7 @@ export function SettingsModal(props: SettingsModalProps) {
database: false,
bot: false,
statistics: false,
+ about: false,
};
});
@@ -267,6 +269,13 @@ export function SettingsModal(props: SettingsModalProps) {
)}
)}
+
+ {shouldRenderSection('about') && (
+
+ {renderSectionHeader('about')}
+ {isSectionVisible('about') && }
+
+ )}
);
}
diff --git a/frontend/src/components/settings/SettingsAboutSection.tsx b/frontend/src/components/settings/SettingsAboutSection.tsx
new file mode 100644
index 0000000..a7e182a
--- /dev/null
+++ b/frontend/src/components/settings/SettingsAboutSection.tsx
@@ -0,0 +1,108 @@
+import { Separator } from '../ui/separator';
+
+const GITHUB_URL = 'https://github.com/jkingsman/Remote-Terminal-for-MeshCore';
+
+export function SettingsAboutSection({ className }: { className?: string }) {
+ const version = __APP_VERSION__;
+ const commit = __COMMIT_HASH__;
+
+ return (
+
+
+ {/* Version */}
+
+
RemoteTerm for MeshCore
+
+ v{version}
+ ·
+ {commit}
+
+
+
+
+
+ {/* Author & License */}
+
+
+
+
+ {/* Links */}
+
+
+
+
+ {/* Acknowledgements */}
+
+
+
+ );
+}
diff --git a/frontend/src/components/settings/settingsConstants.ts b/frontend/src/components/settings/settingsConstants.ts
index e6e43b8..8fe66c4 100644
--- a/frontend/src/components/settings/settingsConstants.ts
+++ b/frontend/src/components/settings/settingsConstants.ts
@@ -5,7 +5,8 @@ export type SettingsSection =
| 'mqtt'
| 'database'
| 'bot'
- | 'statistics';
+ | 'statistics'
+ | 'about';
export const SETTINGS_SECTION_ORDER: SettingsSection[] = [
'radio',
@@ -15,6 +16,7 @@ export const SETTINGS_SECTION_ORDER: SettingsSection[] = [
'database',
'bot',
'statistics',
+ 'about',
];
export const SETTINGS_SECTION_LABELS: Record = {
@@ -25,4 +27,5 @@ export const SETTINGS_SECTION_LABELS: Record = {
database: '🗄️ Database & Interface',
bot: '🤖 Bot',
statistics: '📊 Statistics',
+ about: 'About',
};
diff --git a/frontend/src/types/globals.d.ts b/frontend/src/types/globals.d.ts
new file mode 100644
index 0000000..11311eb
--- /dev/null
+++ b/frontend/src/types/globals.d.ts
@@ -0,0 +1,2 @@
+declare const __APP_VERSION__: string;
+declare const __COMMIT_HASH__: string;
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index ede2da3..10b4892 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,9 +1,24 @@
import path from "path"
+import { execSync } from "child_process"
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+function getCommitHash(): string {
+ // Docker builds pass VITE_COMMIT_HASH as an env var
+ if (process.env.VITE_COMMIT_HASH) return process.env.VITE_COMMIT_HASH;
+ try {
+ return execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim();
+ } catch {
+ return 'unknown';
+ }
+}
+
export default defineConfig({
plugins: [react()],
+ define: {
+ __APP_VERSION__: JSON.stringify(process.env.npm_package_version ?? 'unknown'),
+ __COMMIT_HASH__: JSON.stringify(getCommitHash()),
+ },
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
diff --git a/scripts/publish.sh b/scripts/publish.sh
index 6e0b264..e979d09 100644
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -153,7 +153,8 @@ GIT_HASH=$(git rev-parse --short HEAD)
# Build docker image
echo -e "${YELLOW}Building Docker image...${NC}"
-docker build -t jkingsman/remoteterm-meshcore:latest \
+docker build --build-arg COMMIT_HASH=$GIT_HASH \
+ -t jkingsman/remoteterm-meshcore:latest \
-t jkingsman/remoteterm-meshcore:$VERSION \
-t jkingsman/remoteterm-meshcore:$GIT_HASH .
echo -e "${GREEN}Docker build complete!${NC}"