diff --git a/package-lock.json b/package-lock.json
index dfece51..d596824 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,14 @@
{
"name": "triggerfrontend",
- "version": "1.1.0",
+ "version": "1.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "triggerfrontend",
- "version": "1.1.0",
+ "version": "1.2.0",
"dependencies": {
- "@gfazioli/mantine-text-animate": "^2.3.13",
+ "@gfazioli/mantine-text-animate": "^2.4.0",
"@hello-pangea/dnd": "^18.0.1",
"@mantine/core": "^8.3.14",
"@mantine/dates": "^8.3.14",
@@ -29,16 +29,16 @@
"recharts": "^3.7.0"
},
"devDependencies": {
- "@types/node": "^25.2.1",
+ "@types/node": "^25.2.3",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^5.1.3",
+ "@vitejs/plugin-react": "^5.1.4",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/ui": "^4.0.18",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-dts": "~4.5.4",
- "vite-tsconfig-paths": "^6.0.5",
+ "vite-tsconfig-paths": "^6.1.0",
"vitest": "^4.0.18"
}
},
@@ -820,9 +820,9 @@
"license": "MIT"
},
"node_modules/@gfazioli/mantine-text-animate": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.3.13.tgz",
- "integrity": "sha512-EUx9aYfRiy021sYNjbVt3D+fl1S3S74g7h68yOsm2XEhcbMrjiyahoJkoxsySGIQRRnUbnIkG+ncvi68v9eApg==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.4.0.tgz",
+ "integrity": "sha512-m6zZf6MB8Xm40fEGjSAmcs4gYx0ovWzgIQKhTlsaPDF0w1oD4n1Lj1SX4+vdefSGrGef7Xaazt7mrW2HE2ACdA==",
"peerDependencies": {
"@mantine/core": ">=7.0.0",
"@mantine/hooks": ">=7.0.0",
@@ -1152,9 +1152,9 @@
}
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-rc.2",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz",
- "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==",
+ "version": "1.0.0-rc.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
+ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
"dev": true
},
"node_modules/@rollup/pluginutils": {
@@ -1837,9 +1837,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
- "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
+ "version": "25.2.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
+ "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
"dev": true,
"dependencies": {
"undici-types": "~7.16.0"
@@ -1871,15 +1871,15 @@
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.3.tgz",
- "integrity": "sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==",
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz",
+ "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==",
"dev": true,
"dependencies": {
"@babel/core": "^7.29.0",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-rc.2",
+ "@rolldown/pluginutils": "1.0.0-rc.3",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.18.0"
},
@@ -4189,9 +4189,9 @@
}
},
"node_modules/vite-tsconfig-paths": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.0.5.tgz",
- "integrity": "sha512-f/WvY6ekHykUF1rWJUAbCU7iS/5QYDIugwpqJA+ttwKbxSbzNlqlE8vZSrsnxNQciUW+z6lvhlXMaEyZn9MSig==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.0.tgz",
+ "integrity": "sha512-kpd3sY9glHIDaq4V/Tlc1Y8WaKtutoc3B525GHxEVKWX42FKfQsXvjFOemu1I8VIN8pNbrMLWVTbW79JaRUxKg==",
"dev": true,
"dependencies": {
"debug": "^4.1.1",
diff --git a/package.json b/package.json
index 4768810..4d83448 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
- "@gfazioli/mantine-text-animate": "^2.3.13",
+ "@gfazioli/mantine-text-animate": "^2.4.0",
"@hello-pangea/dnd": "^18.0.1",
"@mantine/core": "^8.3.14",
"@mantine/dates": "^8.3.14",
@@ -31,16 +31,16 @@
"recharts": "^3.7.0"
},
"devDependencies": {
- "@types/node": "^25.2.1",
+ "@types/node": "^25.2.3",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^5.1.3",
+ "@vitejs/plugin-react": "^5.1.4",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/ui": "^4.0.18",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-dts": "~4.5.4",
- "vite-tsconfig-paths": "^6.0.5",
+ "vite-tsconfig-paths": "^6.1.0",
"vitest": "^4.0.18"
}
}
diff --git a/src/components/HelpModal/HelpModal.tsx b/src/components/HelpModal/HelpModal.tsx
index 0daebbf..02c6150 100644
--- a/src/components/HelpModal/HelpModal.tsx
+++ b/src/components/HelpModal/HelpModal.tsx
@@ -43,9 +43,24 @@ export function HelpModal({
+
+ Expand each of the sections below to find available Search Bar commands.
+
{
return {
name: provider.name,
@@ -56,6 +71,7 @@ export function HelpModal({
{
return {
name: application.url,
@@ -66,6 +82,7 @@ export function HelpModal({
'}
items={clientData.map((client: GuacamoleClient) => {
return {
name: `${client.clientName} (${client.clientIdentifier})`,
diff --git a/src/components/HelpModal/HelpSection.tsx b/src/components/HelpModal/HelpSection.tsx
index 363c1c2..1e31f08 100644
--- a/src/components/HelpModal/HelpSection.tsx
+++ b/src/components/HelpModal/HelpSection.tsx
@@ -1,13 +1,24 @@
-import { Group, Text } from '@mantine/core';
+import { ActionIcon, Group, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
+import { useState } from 'react';
+import { FiChevronDown, FiChevronUp } from 'react-icons/fi';
import { textClasses } from 'styles';
import type { SearchShortcut } from 'types';
import HintItem from './HintItem';
-function HelpSection({ name, items }: { name: string; items: SearchShortcut[] }) {
+function HelpSection({
+ name,
+ prefix,
+ items,
+}: {
+ name: string;
+ prefix: string;
+ items: SearchShortcut[];
+}) {
const { theme } = useCommonHooks();
+ const [expand, setExpand] = useState(false);
return (
-
+
- {name}
+ {name}{' '}
+ setExpand(!expand)}>
+ {expand ? : }
+
- {items
- ?.toSorted((a: SearchShortcut, b: SearchShortcut) =>
- a.command > b.command ? 1 : -1,
- )
- .map((item: SearchShortcut) => (
-
- ))}
+ {expand ? (
+ items
+ ?.toSorted((a: SearchShortcut, b: SearchShortcut) =>
+ a.command > b.command ? 1 : -1,
+ )
+ .map((item: SearchShortcut) => (
+
+ ))
+ ) : (
+
+
+ Command prefix: {prefix}
+
+
+ )}
);
diff --git a/src/hooks/useCommonHooks.ts b/src/hooks/useCommonHooks.ts
index 7d32e9a..b3d1b45 100644
--- a/src/hooks/useCommonHooks.ts
+++ b/src/hooks/useCommonHooks.ts
@@ -69,6 +69,10 @@ export function useCommonHooks() {
key: 'providerIcon',
defaultValue: 'FaGoogle',
});
+ const [timeFormat, setTimeFormat] = useLocalStorage({
+ key: 'timeFormat',
+ defaultValue: '12h',
+ });
const [searchValue, setSearchValue] = useState('');
const [providerLabel, setProviderLabel] = useState(defaultProviderLabel);
const [providerURL, setProviderURL] = useState(defaultProviderURL);
@@ -268,6 +272,8 @@ export function useCommonHooks() {
setDefaultProviderPrefix,
defaultProviderIcon,
setDefaultProviderIcon,
+ timeFormat,
+ setTimeFormat,
currentUser,
setCurrentUser,
currentLocation,
diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx
index 1989638..1361006 100644
--- a/src/pages/homepage/Homepage.tsx
+++ b/src/pages/homepage/Homepage.tsx
@@ -92,6 +92,8 @@ export function Homepage() {
windDirection: 0,
pressure: 0,
});
+ const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
+ useDisclosure(false);
const extendedForecast = forecast?.properties?.periods.map(
(item: ExtendedForecast) => ({
detailedForecast: item.detailedForecast,
@@ -209,6 +211,9 @@ export function Homepage() {
openWeatherModal={openWeatherModal}
position='top'
personal={personal}
+ providerModalOpened={providerModalOpened}
+ openProviderModal={openProviderModal}
+ closeProviderModal={closeProviderModal}
/>
)}
-
+
);
}
diff --git a/src/pages/homepage/components/Header/Header.tsx b/src/pages/homepage/components/Header/Header.tsx
index 51b205a..9e2b6f3 100644
--- a/src/pages/homepage/components/Header/Header.tsx
+++ b/src/pages/homepage/components/Header/Header.tsx
@@ -23,6 +23,8 @@ function Header({
theme,
textShadowStyle,
largeTextShadowStyle,
+ timeFormat,
+ setTimeFormat,
} = useCommonHooks();
useEffect(() => {
@@ -119,14 +121,19 @@ function Header({
style={{
textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined,
}}
+ onClick={() =>
+ timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h')
+ }
>
- {`${
- time.getHours() === 0
- ? 12
- : time.getHours() > 12
- ? time.getHours() - 12
- : time.getHours()
- }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`}
+ {timeFormat === '12h'
+ ? `${
+ time.getHours() === 0
+ ? 12
+ : time.getHours() > 12
+ ? time.getHours() - 12
+ : time.getHours()
+ }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`
+ : `${fixDigits(time.getHours())}:${fixDigits(time.getMinutes())}`}
diff --git a/src/pages/homepage/components/Header/LargeHeader.tsx b/src/pages/homepage/components/Header/LargeHeader.tsx
index 1738282..1c2a760 100644
--- a/src/pages/homepage/components/Header/LargeHeader.tsx
+++ b/src/pages/homepage/components/Header/LargeHeader.tsx
@@ -12,6 +12,8 @@ function LargeHeader({ greet, date }: { greet: string | undefined; date: string
theme,
textShadowStyle,
largeTextShadowStyle,
+ timeFormat,
+ setTimeFormat,
} = useCommonHooks();
useEffect(() => {
@@ -58,15 +60,21 @@ function LargeHeader({ greet, date }: { greet: string | undefined; date: string
classNames={textClasses}
style={{
textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined,
+ zIndex: 1,
}}
+ onClick={() =>
+ timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h')
+ }
>
- {`${
- time.getHours() === 0
- ? 12
- : time.getHours() > 12
- ? time.getHours() - 12
- : time.getHours()
- }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`}
+ {timeFormat === '12h'
+ ? `${
+ time.getHours() === 0
+ ? 12
+ : time.getHours() > 12
+ ? time.getHours() - 12
+ : time.getHours()
+ }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`
+ : `${fixDigits(time.getHours())}:${fixDigits(time.getMinutes())}`}
);
diff --git a/src/pages/homepage/components/Search/Search.tsx b/src/pages/homepage/components/Search/Search.tsx
index 80fa286..a168345 100644
--- a/src/pages/homepage/components/Search/Search.tsx
+++ b/src/pages/homepage/components/Search/Search.tsx
@@ -38,6 +38,9 @@ export default function Search({
openWeatherModal,
position,
personal,
+ providerModalOpened,
+ openProviderModal,
+ closeProviderModal,
}: {
current: CurrentWeather;
extendedForecast: ExtendedForecast[];
@@ -46,6 +49,9 @@ export default function Search({
openWeatherModal: () => void;
position: string;
personal: PersonalData;
+ providerModalOpened: boolean;
+ openProviderModal: () => void;
+ closeProviderModal: () => void;
}) {
const {
isMobile,
@@ -138,8 +144,6 @@ export default function Search({
const [historyMultiplier, setHistoryMultiplier] = useState(1);
const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure();
- const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
- useDisclosure(false);
const handleResetSearch = () => {
setSearchValue('');
diff --git a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx
index c5687c0..c8224b4 100644
--- a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx
+++ b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx
@@ -1,7 +1,7 @@
import { Box, Button, Group, Menu, Overlay, Stack } from '@mantine/core';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useState } from 'react';
-import { FiMenu, FiSettings } from 'react-icons/fi';
+import { FiHelpCircle, FiMenu, FiSettings } from 'react-icons/fi';
import { useNavigate } from 'react-router-dom';
import { buttonClasses, menuClasses } from 'styles';
import type { AppCategory, WolClient } from 'types';
@@ -9,7 +9,7 @@ import SettingsCard from './SettingsCard';
import ThemingCard from './ThemingCard';
import WakeOnLan from './WakeOnLanCard';
-function SettingsButton() {
+function SettingsButton({ openProviderModal }: { openProviderModal: () => void }) {
const { data: appCategoryData } = useQueryAPI({
endpoint: 'api/appCategories',
});
@@ -32,7 +32,7 @@ function SettingsButton() {
: [{ value: 'All', label: 'All' }];
const [selectedClient, setSelectedClient] = useState(wolClientData?.[0]);
const navigate = useNavigate();
-
+ const [isOpen, setIsOpen] = useState(false);
const {
theme,
isMobile,
@@ -72,10 +72,16 @@ function SettingsButton() {
withOverlay
overlayProps={{
blur: 5,
+ onClick: () => setIsOpen(false),
}}
+ opened={isOpen}
>
-
@@ -149,6 +155,17 @@ function SettingsButton() {
>
Settings
+ }
+ className={buttonClasses.settingsMenuButton}
+ classNames={buttonClasses}
+ onClick={() => {
+ setIsOpen(false);
+ openProviderModal();
+ }}
+ >
+ Help
+