diff --git a/packages/web/components.json b/packages/web/components.json new file mode 100644 index 00000000..8e3c1f8a --- /dev/null +++ b/packages/web/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@app/components", + "utils": "@app/lib/utils", + "ui": "@app/components/ui", + "lib": "@app/lib", + "hooks": "@app/hooks" + }, + "registries": {} +} diff --git a/packages/web/package.json b/packages/web/package.json index 7d082b11..0150238b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -45,6 +45,7 @@ "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toast": "^1.2.14", @@ -108,6 +109,7 @@ "tailwindcss-animate": "^1.0.7", "tar": "^7.4.3", "testing-library": "^0.0.2", + "tw-animate-css": "^1.3.8", "typescript": "^5.8.3", "vitest": "^3.2.4" } diff --git a/packages/web/public/i18n/locales/en/ui.json b/packages/web/public/i18n/locales/en/ui.json index 694e8982..394317a1 100644 --- a/packages/web/public/i18n/locales/en/ui.json +++ b/packages/web/public/i18n/locales/en/ui.json @@ -22,6 +22,13 @@ } }, "deviceInfo": { + "connectionStatus": { + "title": "Connection Status", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Connection Error" + }, "volts": "{{voltage}} volts", "firmware": { "title": "Firmware", diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx index d0eb9ebe..800dd3a7 100644 --- a/packages/web/src/App.tsx +++ b/packages/web/src/App.tsx @@ -6,13 +6,13 @@ import { KeyBackupReminder } from "@components/KeyBackupReminder.tsx"; import { Toaster } from "@components/Toaster.tsx"; import { ErrorPage } from "@components/UI/ErrorPage.tsx"; import Footer from "@components/UI/Footer.tsx"; -import { useTheme } from "@core/hooks/useTheme.ts"; import { SidebarProvider, useAppStore, useDeviceStore } from "@core/stores"; import { Dashboard } from "@pages/Dashboard/index.tsx"; import { Outlet } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import { ErrorBoundary } from "react-error-boundary"; import { MapProvider } from "react-map-gl/maplibre"; +import { ThemeProvider } from "./components/theme-provider.tsx"; export function App() { const { getDevice } = useDeviceStore(); @@ -21,45 +21,44 @@ export function App() { const device = getDevice(selectedDeviceId); - // Sets up light/dark mode based on user preferences or system settings - useTheme(); - return ( - - { - setConnectDialogOpen(open); - }} - /> - - - -
- -
- {device ? ( -
- - - - - - -
- ) : ( - <> - -
-
-
-
-
+ + + { + setConnectDialogOpen(open); + }} + /> + + + +
+ +
+ {device ? ( +
+ + + + + + +
+ ) : ( + <> + +
+
+
+
+
+
); } diff --git a/packages/web/src/components/ConnectionStatus.tsx b/packages/web/src/components/ConnectionStatus.tsx new file mode 100644 index 00000000..0bcfbaa3 --- /dev/null +++ b/packages/web/src/components/ConnectionStatus.tsx @@ -0,0 +1,8 @@ +import { useDevice } from "@app/core/stores"; + +export function ConnectionStatus() { + const { status } = useDevice(); + console.log(status); + + return
Connection Status Component
; +} diff --git a/packages/web/src/components/DeviceInfoPanel.tsx b/packages/web/src/components/DeviceInfoPanel.tsx index 6480cb39..03558e62 100644 --- a/packages/web/src/components/DeviceInfoPanel.tsx +++ b/packages/web/src/components/DeviceInfoPanel.tsx @@ -1,5 +1,6 @@ import { cn } from "@core/utils/cn.ts"; import { + CableIcon, CpuIcon, Languages, type LucideIcon, @@ -8,17 +9,25 @@ import { Search as SearchIcon, ZapIcon, } from "lucide-react"; +import type { DeviceStatusEnum } from "node_modules/@meshtastic/core/src/types"; import type React from "react"; import { Fragment } from "react"; import { useTranslation } from "react-i18next"; import BatteryStatus from "./BatteryStatus.tsx"; import LanguageSwitcher from "./LanguageSwitcher.tsx"; import ThemeSwitcher from "./ThemeSwitcher.tsx"; -import type { DeviceMetrics } from "./types.ts"; +import { ThemeModeToggle } from "./theme-mode-toggle.tsx"; import { Avatar } from "./UI/Avatar.tsx"; import { Button } from "./UI/Button.tsx"; +import { Separator } from "./UI/Separator.tsx"; import { Subtle } from "./UI/Typography/Subtle.tsx"; +export type DeviceMetrics = { + connectionStatus: DeviceStatusEnum; + batteryLevel?: number | null; + voltage?: number | null; +}; + interface DeviceInfoPanelProps { isCollapsed: boolean; deviceMetrics: DeviceMetrics; @@ -61,6 +70,12 @@ export const DeviceInfoPanel = ({ const { batteryLevel, voltage } = deviceMetrics; const deviceInfoItems: InfoDisplayItem[] = [ + { + id: "deviceConnectionStatus", + label: t("sidebar.deviceInfo.connectionStatus.title"), + icon: CableIcon, + value: deviceMetrics.connectionStatus || t("unknown.notAvailable", "N/A"), + }, { id: "battery", label: t("batteryStatus.title"), @@ -89,7 +104,7 @@ export const DeviceInfoPanel = ({ id: "theme", label: t("theme.changeTheme"), icon: Palette, - render: () => , + render: () => , }, { id: "changeName", @@ -137,9 +152,7 @@ export const DeviceInfoPanel = ({ )} - {!isCollapsed && ( -
- )} + {!isCollapsed && }
{IconComponent && ( - + )} {item.customComponent} {item.id !== "battery" && ( - + {item.label}: {item.value} )} @@ -171,9 +181,7 @@ export const DeviceInfoPanel = ({ })}
- {!isCollapsed && ( -
- )} + {!isCollapsed && }
{buttonItem.label} diff --git a/packages/web/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx b/packages/web/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx index e2eaa0a7..af5dacc5 100644 --- a/packages/web/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx +++ b/packages/web/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx @@ -1,3 +1,10 @@ +import { + Tooltip, + TooltipArrow, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@app/components/UI/tooltip"; import { DeviceImage } from "@components/generic/DeviceImage.tsx"; import { TimeAgo } from "@components/generic/TimeAgo.tsx"; import { Uptime } from "@components/generic/Uptime.tsx"; @@ -17,13 +24,6 @@ import { DialogTitle, } from "@components/UI/Dialog.tsx"; import { Separator } from "@components/UI/Separator.tsx"; -import { - Tooltip, - TooltipArrow, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@components/UI/Tooltip.tsx"; import { useFavoriteNode } from "@core/hooks/useFavoriteNode.ts"; import { useIgnoreNode } from "@core/hooks/useIgnoreNode.ts"; import { toast } from "@core/hooks/useToast.ts"; diff --git a/packages/web/src/components/Map.tsx b/packages/web/src/components/Map.tsx index c0ced881..2d1f8d6e 100644 --- a/packages/web/src/components/Map.tsx +++ b/packages/web/src/components/Map.tsx @@ -1,9 +1,6 @@ -import { useTheme } from "@core/hooks/useTheme.ts"; -import { useEffect, useMemo, useRef } from "react"; -import { useTranslation } from "react-i18next"; +import { useEffect, useRef } from "react"; import MapGl, { AttributionControl, - type MapLayerMouseEvent, type MapRef, NavigationControl, ScaleControl, @@ -12,22 +9,9 @@ import MapGl, { interface MapProps { children?: React.ReactNode; onLoad?: (map: MapRef) => void; - onMouseMove?: (event: MapLayerMouseEvent) => void; - onClick?: (event: MapLayerMouseEvent) => void; - interactiveLayerIds?: string[]; } -export const BaseMap = ({ - children, - onLoad, - onClick, - onMouseMove, - interactiveLayerIds, -}: MapProps) => { - const { theme } = useTheme(); - const { t } = useTranslation("map"); - - const darkMode = theme === "dark"; +export const MeshMap = ({ children, onLoad }: MapProps) => { const mapRef = useRef(null); useEffect(() => { @@ -37,27 +21,6 @@ export const BaseMap = ({ } }, [onLoad]); - const locale = useMemo(() => { - return { - "GeolocateControl.FindMyLocation": t( - "maplibre.GeolocateControl.FindMyLocation", - ), - "NavigationControl.ZoomIn": t("maplibre.NavigationControl.ZoomIn"), - "NavigationControl.ZoomOut": t("maplibre.NavigationControl.ZoomOut"), - "ScaleControl.Meters": t("unit.meter.suffix"), - "ScaleControl.Kilometers": t("unit.kilometer.suffix"), - "CooperativeGesturesHandler.WindowsHelpText": t( - "maplibre.CooperativeGesturesHandler.WindowsHelpText", - ), - "CooperativeGesturesHandler.MacHelpText": t( - "maplibre.CooperativeGesturesHandler.MacHelpText", - ), - "CooperativeGesturesHandler.MobileHelpText": t( - "maplibre.CooperativeGesturesHandler.MobileHelpText", - ), - }; - }, [t]); - return ( - + {/* { Disabled for now until we can use i18n for the geolocate control} */} {/* ( -
  • -
    - -
    - {label} -
    - -
    +
  • + + + {label} + +
  • ); @@ -125,7 +123,7 @@ export const ChannelChat = ({ messages = [] }: ChannelChatProps) => { } return ( -
      +
        {groups.map(({ dayKey, label, items }) => ( {/* Render messages first, then delimiter — with flex-col-reverse this shows the delimiter above that day's messages */} diff --git a/packages/web/src/components/PageComponents/Messages/MessageActionsMenu.tsx b/packages/web/src/components/PageComponents/Messages/MessageActionsMenu.tsx index 2bdf10b7..23036cdf 100644 --- a/packages/web/src/components/PageComponents/Messages/MessageActionsMenu.tsx +++ b/packages/web/src/components/PageComponents/Messages/MessageActionsMenu.tsx @@ -4,7 +4,7 @@ import { TooltipContent, TooltipProvider, TooltipTrigger, -} from "@components/UI/Tooltip.tsx"; +} from "@app/components/UI/tooltip"; import { cn } from "@core/utils/cn.ts"; import { Reply, SmilePlus } from "lucide-react"; import { useTranslation } from "react-i18next"; diff --git a/packages/web/src/components/PageComponents/Messages/MessageInput.tsx b/packages/web/src/components/PageComponents/Messages/MessageInput.tsx index 4a78d068..398265e7 100644 --- a/packages/web/src/components/PageComponents/Messages/MessageInput.tsx +++ b/packages/web/src/components/PageComponents/Messages/MessageInput.tsx @@ -75,6 +75,7 @@ export const MessageInput = ({ onSend, to, maxBytes }: MessageInputProps) => {
    diff --git a/packages/web/src/components/PageComponents/Messages/MessageItem.tsx b/packages/web/src/components/PageComponents/Messages/MessageItem.tsx index acf89a63..663881c9 100644 --- a/packages/web/src/components/PageComponents/Messages/MessageItem.tsx +++ b/packages/web/src/components/PageComponents/Messages/MessageItem.tsx @@ -1,11 +1,11 @@ -import { Avatar } from "@components/UI/Avatar.tsx"; import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, -} from "@components/UI/Tooltip.tsx"; +} from "@app/components/UI/tooltip"; +import { Avatar } from "@components/UI/Avatar.tsx"; import { MessageState, useDevice, useNodeDB } from "@core/stores"; import type { Message } from "@core/stores/messageStore/types.ts"; import { cn } from "@core/utils/cn.ts"; diff --git a/packages/web/src/components/PageLayout.tsx b/packages/web/src/components/PageLayout.tsx index b2bc76c2..bdca287a 100644 --- a/packages/web/src/components/PageLayout.tsx +++ b/packages/web/src/components/PageLayout.tsx @@ -45,12 +45,12 @@ export const PageLayout = ({ }: PageLayoutProps) => { return ( -
    +
    {/* Left Sidebar */} {leftBar && (