diff --git a/nym-vpn/ui/src-tauri/src/commands/app_data.rs b/nym-vpn/ui/src-tauri/src/commands/app_data.rs
index 539b3f413f..d4acdaa5f1 100644
--- a/nym-vpn/ui/src-tauri/src/commands/app_data.rs
+++ b/nym-vpn/ui/src-tauri/src/commands/app_data.rs
@@ -121,3 +121,47 @@ pub async fn set_entry_location_selector(
.map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
Ok(())
}
+
+#[instrument(skip(data_state))]
+#[tauri::command]
+pub async fn set_auto_connect(
+ data_state: State<'_, SharedAppData>,
+ entry_selector: bool,
+) -> Result<(), CmdError> {
+ debug!("set_auto_connect");
+
+ let mut app_data_store = data_state.lock().await;
+ let mut app_data = app_data_store
+ .read()
+ .await
+ .map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
+ app_data.autoconnect = Some(entry_selector);
+ app_data_store.data = app_data;
+ app_data_store
+ .write()
+ .await
+ .map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
+ Ok(())
+}
+
+#[instrument(skip(data_state))]
+#[tauri::command]
+pub async fn set_monitoring(
+ data_state: State<'_, SharedAppData>,
+ entry_selector: bool,
+) -> Result<(), CmdError> {
+ debug!("set_monitoring");
+
+ let mut app_data_store = data_state.lock().await;
+ let mut app_data = app_data_store
+ .read()
+ .await
+ .map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
+ app_data.monitoring = Some(entry_selector);
+ app_data_store.data = app_data;
+ app_data_store
+ .write()
+ .await
+ .map_err(|e| CmdError::new(CmdErrorSource::InternalError, e.to_string()))?;
+ Ok(())
+}
diff --git a/nym-vpn/ui/src-tauri/src/main.rs b/nym-vpn/ui/src-tauri/src/main.rs
index 6cdc3815ab..64eb32c2e3 100644
--- a/nym-vpn/ui/src-tauri/src/main.rs
+++ b/nym-vpn/ui/src-tauri/src/main.rs
@@ -108,6 +108,8 @@ async fn main() -> Result<()> {
app_data::set_app_data,
app_data::set_ui_theme,
app_data::set_entry_location_selector,
+ app_data::set_monitoring,
+ app_data::set_auto_connect,
app_data::get_node_countries,
app_data::set_root_font_size,
node_location::set_node_location,
diff --git a/nym-vpn/ui/src-tauri/tauri.conf.json b/nym-vpn/ui/src-tauri/tauri.conf.json
index 93898ba01e..646fe5971a 100644
--- a/nym-vpn/ui/src-tauri/tauri.conf.json
+++ b/nym-vpn/ui/src-tauri/tauri.conf.json
@@ -8,7 +8,7 @@
},
"package": {
"productName": "nym-vpn",
- "version": "0.0.0"
+ "version": "0.0.1"
},
"tauri": {
"updater": {
diff --git a/nym-vpn/ui/src/constants.ts b/nym-vpn/ui/src/constants.ts
index ee33ed2fcd..3193f21cf6 100644
--- a/nym-vpn/ui/src/constants.ts
+++ b/nym-vpn/ui/src/constants.ts
@@ -1,6 +1,10 @@
export const routes = {
root: '/',
settings: '/settings',
+ display: '/settings/display',
+ logs: '/settings/logs',
+ feedback: '/settings/feedback',
+ legal: '/settings/legal',
entryNodeLocation: '/entry-node-location',
exitNodeLocation: '/exit-node-location',
} as const;
diff --git a/nym-vpn/ui/src/i18n/en/common.json b/nym-vpn/ui/src/i18n/en/common.json
index 824ef148fb..b959ce36db 100644
--- a/nym-vpn/ui/src/i18n/en/common.json
+++ b/nym-vpn/ui/src/i18n/en/common.json
@@ -3,5 +3,9 @@
"disconnect": "Disconnect",
"first-hop-selection": "First hop selection",
"last-hop-selection": "Last hop selection",
- "settings": "Settings"
+ "settings": "Settings",
+ "display-theme": "Display theme",
+ "logs": "Logs",
+ "feedback": "Feedback",
+ "legal": "Legal"
}
diff --git a/nym-vpn/ui/src/i18n/en/settings.json b/nym-vpn/ui/src/i18n/en/settings.json
index dd9a88fffe..a2c67b4e14 100644
--- a/nym-vpn/ui/src/i18n/en/settings.json
+++ b/nym-vpn/ui/src/i18n/en/settings.json
@@ -1,3 +1,20 @@
{
- "entry-selector": "Entry location selector"
+ "auto-connect": {
+ "title": "Auto-connect",
+ "desc": "Auto connect at app startup"
+ },
+ "entry-selector": {
+ "title": "Entry location selector",
+ "desc": "Manually select your entry hop"
+ },
+ "feedback": "Feedback",
+ "error-reporting": {
+ "title": "Error reporting",
+ "desc": "Help us improve NymVPN"
+ },
+ "faq": "FAQ",
+ "display-theme": "Display theme",
+ "logs": "Logs",
+ "legal": "Legal",
+ "quit": "Quit NymVPN"
}
diff --git a/nym-vpn/ui/src/pages/home/Home.tsx b/nym-vpn/ui/src/pages/home/Home.tsx
index ef6d8ede54..63ff90e0ac 100644
--- a/nym-vpn/ui/src/pages/home/Home.tsx
+++ b/nym-vpn/ui/src/pages/home/Home.tsx
@@ -12,8 +12,14 @@ import ConnectionStatus from './ConnectionStatus';
import HopSelect from './HopSelect';
function Home() {
- const { state, loading, exitNodeLocation, defaultNodeLocation } =
- useMainState();
+ const {
+ state,
+ loading,
+ entryNodeLocation,
+ exitNodeLocation,
+ entrySelector,
+ defaultNodeLocation,
+ } = useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const navigate = useNavigate();
const { t } = useTranslation('home');
@@ -77,6 +83,17 @@ function Home() {
{t('select-node-title')}
+ {entrySelector && (
+ {
+ if (state === 'Disconnected') {
+ navigate(routes.entryNodeLocation);
+ }
+ }}
+ nodeHop="entry"
+ />
+ )}
{
diff --git a/nym-vpn/ui/src/pages/settings/Settings.tsx b/nym-vpn/ui/src/pages/settings/Settings.tsx
index 7d1044bc8a..9166ddcdb5 100644
--- a/nym-vpn/ui/src/pages/settings/Settings.tsx
+++ b/nym-vpn/ui/src/pages/settings/Settings.tsx
@@ -2,61 +2,216 @@ import { useEffect, useState } from 'react';
import clsx from 'clsx';
import { invoke } from '@tauri-apps/api';
import { Switch } from '@headlessui/react';
+import { useTranslation } from 'react-i18next';
+import { useNavigate } from 'react-router-dom';
+import { routes } from '../../constants';
import { useMainDispatch, useMainState } from '../../contexts';
import { StateDispatch } from '../../types';
-import UiScaler from './UiScaler';
+import SettingsGroup from './SettingsGroup';
function Settings() {
const state = useMainState();
+ const navigate = useNavigate();
const dispatch = useMainDispatch() as StateDispatch;
+ const { t } = useTranslation('settings');
- const [darkModeEnabled, setDarkModeEnabled] = useState(
- state.uiTheme === 'Dark',
- );
+ const [entrySelector, setEntrySelector] = useState(state.entrySelector);
+ const [autoConnect, setAutoConnect] = useState(state.autoConnect);
+ const [monitoring, setMonitoring] = useState(state.monitoring);
useEffect(() => {
- setDarkModeEnabled(state.uiTheme === 'Dark');
+ setEntrySelector(state.entrySelector);
+ setAutoConnect(state.autoConnect);
+ setMonitoring(state.monitoring);
}, [state]);
- const handleThemeChange = async (darkMode: boolean) => {
- if (darkMode && state.uiTheme === 'Light') {
- dispatch({ type: 'set-ui-theme', theme: 'Dark' });
- } else if (!darkMode && state.uiTheme === 'Dark') {
- dispatch({ type: 'set-ui-theme', theme: 'Light' });
- }
- invoke('set_ui_theme', { theme: darkMode ? 'Dark' : 'Light' }).catch(
- (e) => {
- console.log(e);
- },
- );
+ const handleEntrySelectorChange = async () => {
+ const isSelected = !state.entrySelector;
+ dispatch({ type: 'set-entry-selector', entrySelector: isSelected });
+ invoke('set_entry_location_selector', {
+ entrySelector: isSelected,
+ }).catch((e) => {
+ console.log(e);
+ });
+ };
+
+ const handleAutoConnectChanged = async () => {
+ const isSelected = !state.autoConnect;
+ dispatch({ type: 'set-auto-connect', autoConnect: isSelected });
+ invoke('set_auto_connect', { autoConnect: isSelected }).catch((e) => {
+ console.log(e);
+ });
+ };
+
+ const handleMonitoringChanged = async () => {
+ const isSelected = !state.monitoring;
+ dispatch({ type: 'set-monitoring', monitoring: isSelected });
+ invoke('set_monitoring', { monitoring: isSelected }).catch((e) => {
+ console.log(e);
+ });
};
return (
-
-
-
- Dark Mode
-
-
- Dark mode
-
-
+
+
+
+
+ ),
+ },
+ {
+ title: t('entry-selector.title'),
+ desc: t('entry-selector.desc'),
+ leadingIcon: 'looks_two',
+ trailing: (
+
+
+
+ ),
+ },
+ ]}
+ />
+ {
+ navigate(routes.display);
+ },
+ trailing: (
+
+ arrow_right
+
+ ),
+ },
+ ]}
+ />
+ {
+ navigate(routes.logs);
+ },
+ trailing: (
+
+ arrow_right
+
+ ),
+ },
+ ]}
+ />
+ {
+ navigate(routes.feedback);
+ },
+ trailing: (
+
+ arrow_right
+
+ ),
+ },
+ {
+ title: t('error-reporting.title'),
+ desc: t('error-reporting.desc'),
+ leadingIcon: 'error',
+ trailing: (
+
+
+
+ ),
+ },
+ {
+ title: t('faq'),
+ leadingIcon: 'help',
+ trailing: (
+ launch
+ ),
+ },
+ ]}
+ />
+ {
+ navigate(routes.legal);
+ },
+ trailing: (
+
+ arrow_right
+
+ ),
+ },
+ ]}
+ />
+ {
+ //TODO shutdown gracefully
+ },
+ },
+ ]}
+ />
+
+ Version {state.version}
-
);
}
diff --git a/nym-vpn/ui/src/pages/settings/SettingsGroup.tsx b/nym-vpn/ui/src/pages/settings/SettingsGroup.tsx
new file mode 100644
index 0000000000..e1bd46a30f
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/SettingsGroup.tsx
@@ -0,0 +1,66 @@
+import clsx from 'clsx';
+import { ReactNode } from 'react';
+import { RadioGroup } from '@headlessui/react';
+
+type Setting = {
+ title: string;
+ leadingIcon?: string;
+ desc?: string;
+ onClick?: () => void;
+ trailing?: ReactNode;
+};
+
+interface Props {
+ settings: Setting[];
+}
+
+function SettingsGroup({ settings }: Props) {
+ return (
+
+ {settings.map((setting, index) => (
+
+
+ {setting.leadingIcon && (
+
{setting.leadingIcon}
+ )}
+
+
+
+ {setting.title}
+
+
+ {setting.desc}
+
+
+
+
{setting.trailing}
+
+
+ ))}
+
+ );
+}
+
+export default SettingsGroup;
diff --git a/nym-vpn/ui/src/pages/settings/SettingsLayout.tsx b/nym-vpn/ui/src/pages/settings/SettingsLayout.tsx
new file mode 100644
index 0000000000..90015a54fc
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/SettingsLayout.tsx
@@ -0,0 +1,11 @@
+import { Outlet } from 'react-router-dom';
+
+function SettingsLayout() {
+ return (
+
+
+
+ );
+}
+
+export default SettingsLayout;
diff --git a/nym-vpn/ui/src/pages/settings/display/Display.tsx b/nym-vpn/ui/src/pages/settings/display/Display.tsx
new file mode 100644
index 0000000000..a783161baf
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/display/Display.tsx
@@ -0,0 +1,64 @@
+import { useEffect, useState } from 'react';
+import clsx from 'clsx';
+import { invoke } from '@tauri-apps/api';
+import { Switch } from '@headlessui/react';
+import { useMainDispatch, useMainState } from '../../../contexts';
+import { StateDispatch } from '../../../types';
+import UiScaler from './UiScaler';
+
+function Display() {
+ const state = useMainState();
+ const dispatch = useMainDispatch() as StateDispatch;
+
+ const [darkModeEnabled, setDarkModeEnabled] = useState(
+ state.uiTheme === 'Dark',
+ );
+
+ useEffect(() => {
+ setDarkModeEnabled(state.uiTheme === 'Dark');
+ }, [state]);
+
+ const handleThemeChange = async (darkMode: boolean) => {
+ if (darkMode && state.uiTheme === 'Light') {
+ dispatch({ type: 'set-ui-theme', theme: 'Dark' });
+ } else if (!darkMode && state.uiTheme === 'Dark') {
+ dispatch({ type: 'set-ui-theme', theme: 'Light' });
+ }
+ invoke
('set_ui_theme', { theme: darkMode ? 'Dark' : 'Light' }).catch(
+ (e) => {
+ console.log(e);
+ },
+ );
+ };
+
+ return (
+
+
+
+ Dark Mode
+
+
+ Dark mode
+
+
+
+
+
+ );
+}
+
+export default Display;
diff --git a/nym-vpn/ui/src/pages/settings/UiScaler.tsx b/nym-vpn/ui/src/pages/settings/display/UiScaler.tsx
similarity index 91%
rename from nym-vpn/ui/src/pages/settings/UiScaler.tsx
rename to nym-vpn/ui/src/pages/settings/display/UiScaler.tsx
index 7cde54f24b..c5a0652fcc 100644
--- a/nym-vpn/ui/src/pages/settings/UiScaler.tsx
+++ b/nym-vpn/ui/src/pages/settings/display/UiScaler.tsx
@@ -1,7 +1,7 @@
import { ChangeEvent, useEffect, useState } from 'react';
import { invoke } from '@tauri-apps/api';
-import { useMainDispatch, useMainState } from '../../contexts';
-import { CmdError, StateDispatch } from '../../types';
+import { useMainDispatch, useMainState } from '../../../contexts';
+import { CmdError, StateDispatch } from '../../../types';
function UiScaler() {
const [slideValue, setSlideValue] = useState(12);
diff --git a/nym-vpn/ui/src/pages/settings/display/index.ts b/nym-vpn/ui/src/pages/settings/display/index.ts
new file mode 100644
index 0000000000..fa2dc562ff
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/display/index.ts
@@ -0,0 +1 @@
+export { default as Display } from './Display';
diff --git a/nym-vpn/ui/src/pages/settings/feedback/Feedback.tsx b/nym-vpn/ui/src/pages/settings/feedback/Feedback.tsx
new file mode 100644
index 0000000000..a2f48556f3
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/feedback/Feedback.tsx
@@ -0,0 +1,9 @@
+function Feedback() {
+ return (
+
+ );
+}
+
+export default Feedback;
diff --git a/nym-vpn/ui/src/pages/settings/feedback/index.ts b/nym-vpn/ui/src/pages/settings/feedback/index.ts
new file mode 100644
index 0000000000..4a06655c88
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/feedback/index.ts
@@ -0,0 +1 @@
+export { default as Feedback } from './Feedback';
diff --git a/nym-vpn/ui/src/pages/settings/index.ts b/nym-vpn/ui/src/pages/settings/index.ts
index 1f16002f16..65ba8ccb8d 100644
--- a/nym-vpn/ui/src/pages/settings/index.ts
+++ b/nym-vpn/ui/src/pages/settings/index.ts
@@ -1 +1,5 @@
export { default as Settings } from './Settings';
+export { default as SettingsLayout } from './SettingsLayout';
+export * from './display';
+export * from './feedback';
+export * from './legal';
diff --git a/nym-vpn/ui/src/pages/settings/legal/Legal.tsx b/nym-vpn/ui/src/pages/settings/legal/Legal.tsx
new file mode 100644
index 0000000000..e7267f8cee
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/legal/Legal.tsx
@@ -0,0 +1,9 @@
+function Legal() {
+ return (
+
+ );
+}
+
+export default Legal;
diff --git a/nym-vpn/ui/src/pages/settings/legal/index.ts b/nym-vpn/ui/src/pages/settings/legal/index.ts
new file mode 100644
index 0000000000..77d3ce9eb2
--- /dev/null
+++ b/nym-vpn/ui/src/pages/settings/legal/index.ts
@@ -0,0 +1 @@
+export { default as Legal } from './Legal';
diff --git a/nym-vpn/ui/src/router.tsx b/nym-vpn/ui/src/router.tsx
index a7b8dc5d39..02300ee738 100644
--- a/nym-vpn/ui/src/router.tsx
+++ b/nym-vpn/ui/src/router.tsx
@@ -1,5 +1,15 @@
import { createBrowserRouter } from 'react-router-dom';
-import { Error, Home, NavLayout, NodeLocation, Settings } from './pages';
+import {
+ Display,
+ Error,
+ Feedback,
+ Home,
+ Legal,
+ NavLayout,
+ NodeLocation,
+ Settings,
+ SettingsLayout,
+} from './pages';
import { routes } from './constants';
const router = createBrowserRouter([
@@ -14,8 +24,30 @@ const router = createBrowserRouter([
},
{
path: routes.settings,
- element: ,
+ element: ,
errorElement: ,
+ children: [
+ {
+ element: ,
+ errorElement: ,
+ index: true,
+ },
+ {
+ path: routes.display,
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: routes.feedback,
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: routes.legal,
+ element: ,
+ errorElement: ,
+ },
+ ],
},
{
path: routes.entryNodeLocation,
diff --git a/nym-vpn/ui/src/state/main.ts b/nym-vpn/ui/src/state/main.ts
index 824da4c14b..4c8a6d1fb9 100644
--- a/nym-vpn/ui/src/state/main.ts
+++ b/nym-vpn/ui/src/state/main.ts
@@ -19,9 +19,12 @@ export type StateAction =
| { type: 'new-progress-message'; message: ConnectProgressMsg }
| { type: 'connect' }
| { type: 'disconnect' }
+ | { type: 'set-version'; version: string }
| { type: 'set-connected'; startTime: number }
| { type: 'set-connection-start-time'; startTime?: number | null }
| { type: 'set-disconnected' }
+ | { type: 'set-auto-connect'; autoConnect: boolean }
+ | { type: 'set-monitoring'; monitoring: boolean }
| { type: 'reset' }
| { type: 'set-ui-theme'; theme: UiTheme }
| { type: 'set-countries'; countries: Country[] }
@@ -31,12 +34,15 @@ export type StateAction =
export const initialState: AppState = {
state: 'Disconnected',
+ version: null,
loading: false,
vpnMode: 'TwoHop',
entrySelector: false,
tunnel: { name: 'nym', id: 'nym' },
uiTheme: 'Light',
progressMessages: [],
+ autoConnect: false,
+ monitoring: false,
entryNodeLocation: null,
exitNodeLocation: null,
defaultNodeLocation: {
@@ -70,6 +76,16 @@ export function reducer(state: AppState, action: StateAction): AppState {
...state,
entrySelector: action.entrySelector,
};
+ case 'set-auto-connect':
+ return {
+ ...state,
+ autoConnect: action.autoConnect,
+ };
+ case 'set-monitoring':
+ return {
+ ...state,
+ monitoring: action.monitoring,
+ };
case 'set-countries':
return {
...state,
@@ -101,6 +117,11 @@ export function reducer(state: AppState, action: StateAction): AppState {
case 'disconnect': {
return { ...state, state: 'Disconnecting', loading: true };
}
+ case 'set-version':
+ return {
+ ...state,
+ version: action.version,
+ };
case 'set-connected': {
console.log(
`__REDUCER [set-connected] changing connection state to Connected`,
@@ -152,6 +173,7 @@ export function reducer(state: AppState, action: StateAction): AppState {
...state,
rootFontSize: action.size,
};
+
case 'reset':
return initialState;
}
diff --git a/nym-vpn/ui/src/state/provider.tsx b/nym-vpn/ui/src/state/provider.tsx
index dcbf00946e..482ea353bd 100644
--- a/nym-vpn/ui/src/state/provider.tsx
+++ b/nym-vpn/ui/src/state/provider.tsx
@@ -1,5 +1,6 @@
import React, { useEffect, useReducer } from 'react';
import { invoke } from '@tauri-apps/api';
+import { getVersion } from '@tauri-apps/api/app';
import { MainDispatchContext, MainStateContext } from '../contexts';
import {
AppDataFromBackend,
@@ -41,6 +42,17 @@ export function MainStateProvider({ children }: Props) {
return await invoke('get_default_node_location');
};
+ getVersion()
+ .then((version) =>
+ dispatch({
+ type: 'set-version',
+ version,
+ }),
+ )
+ .catch((e) => {
+ console.warn(`command [set-version] returned an error: ${e}`);
+ });
+
getInitialConnectionState()
.then((state) => dispatch({ type: 'change-connection-state', state }))
.catch((e: CmdError) => {
@@ -99,6 +111,8 @@ export function MainStateProvider({ children }: Props) {
entrySelector: data.entry_location_selector || false,
uiTheme: data.ui_theme || 'Light',
vpnMode: data.vpn_mode || 'TwoHop',
+ autoConnect: data.autoconnect || false,
+ monitoring: data.monitoring || false,
rootFontSize: data.ui_root_font_size || DefaultRootFontSize,
};
if (data.entry_node_location) {
diff --git a/nym-vpn/ui/src/types/app-state.ts b/nym-vpn/ui/src/types/app-state.ts
index 7ace38c3c5..bbbd887c8c 100644
--- a/nym-vpn/ui/src/types/app-state.ts
+++ b/nym-vpn/ui/src/types/app-state.ts
@@ -19,6 +19,7 @@ export interface TunnelConfig {
export type AppState = {
state: ConnectionState;
+ version: string | null;
loading: boolean;
error?: string | null;
progressMessages: ConnectProgressMsg[];
@@ -27,6 +28,8 @@ export type AppState = {
tunnel: TunnelConfig;
uiTheme: 'Light' | 'Dark';
entrySelector: boolean;
+ autoConnect: boolean;
+ monitoring: boolean;
entryNodeLocation: Country | null;
exitNodeLocation: Country | null;
defaultNodeLocation: Country;
diff --git a/nym-vpn/ui/src/ui/TopBar.tsx b/nym-vpn/ui/src/ui/TopBar.tsx
index 117ec3ddaa..809c9a2cf0 100644
--- a/nym-vpn/ui/src/ui/TopBar.tsx
+++ b/nym-vpn/ui/src/ui/TopBar.tsx
@@ -45,6 +45,34 @@ export default function TopBar() {
navigate(-1);
},
},
+ '/settings/display': {
+ title: t('display-theme'),
+ leftIcon: 'arrow_back',
+ handleLeftNav: () => {
+ navigate(-1);
+ },
+ },
+ '/settings/logs': {
+ title: t('logs'),
+ leftIcon: 'arrow_back',
+ handleLeftNav: () => {
+ navigate(-1);
+ },
+ },
+ '/settings/feedback': {
+ title: t('feedback'),
+ leftIcon: 'arrow_back',
+ handleLeftNav: () => {
+ navigate(-1);
+ },
+ },
+ '/settings/legal': {
+ title: t('legal'),
+ leftIcon: 'arrow_back',
+ handleLeftNav: () => {
+ navigate(-1);
+ },
+ },
'/entry-node-location': {
title: t('first-hop-selection'),
leftIcon: 'arrow_back',
diff --git a/nym-vpn/ui/tailwind.config.ts b/nym-vpn/ui/tailwind.config.ts
index 87fbcce01e..247a3df63c 100644
--- a/nym-vpn/ui/tailwind.config.ts
+++ b/nym-vpn/ui/tailwind.config.ts
@@ -34,6 +34,7 @@ export default {
cornflower: '#7075FF',
// [DL] error status text
teaberry: '#E33B5A',
+ comet: '#625B71',
// [DL] "Connected" status text
'vert-menthe': '#2BC761',
// [D] main titles text