From 322f5a125bfbd5e65c564e12d5a350985a0424f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Petrov Date: Sat, 11 Jul 2026 16:12:09 +0300 Subject: [PATCH] Polish client duration and tooltip interactions --- index.html | 2 +- public/harbor-connect.svg | 8 +++ public/harbor-gateway.svg | 8 +++ src/web/App.jsx | 7 ++- src/web/components/ClientOverviewPage.jsx | 41 +++++++++++----- src/web/styles.css | 59 +++++++++++++++++++++-- 6 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 public/harbor-connect.svg create mode 100644 public/harbor-gateway.svg diff --git a/index.html b/index.html index 94df9a6..1928a32 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - + Harbor diff --git a/public/harbor-connect.svg b/public/harbor-connect.svg new file mode 100644 index 0000000..e7fbfac --- /dev/null +++ b/public/harbor-connect.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/harbor-gateway.svg b/public/harbor-gateway.svg new file mode 100644 index 0000000..bfcaeba --- /dev/null +++ b/public/harbor-gateway.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/web/App.jsx b/src/web/App.jsx index d940880..f03ee56 100644 --- a/src/web/App.jsx +++ b/src/web/App.jsx @@ -31,7 +31,12 @@ function App() { }, []); useEffect(() => { - if (state?.mode) document.title = state.mode === 'gateway' ? 'Harbor Gateway' : 'Harbor Connect'; + if (!state?.mode) return; + const isGateway = state.mode === 'gateway'; + document.title = isGateway ? 'Harbor Gateway' : 'Harbor Connect'; + document.getElementById('harbor-favicon').href = isGateway + ? '/harbor-gateway.svg' + : '/harbor-connect.svg'; }, [state?.mode]); async function run(action) { diff --git a/src/web/components/ClientOverviewPage.jsx b/src/web/components/ClientOverviewPage.jsx index d59ca1c..de24beb 100644 --- a/src/web/components/ClientOverviewPage.jsx +++ b/src/web/components/ClientOverviewPage.jsx @@ -14,6 +14,11 @@ import { formatBytes } from '../utils/format.js'; import { instructionBlocks } from '../instructions.js'; const SUBSCRIPTION_REVEAL_DELAY_MS = 1350; +const DURATION_MODE_STORAGE_KEY = 'harbor-duration-mode'; + +function CloudTooltip({ children }) { + return {children}; +} function InstructionStep({ step }) { if (typeof step === 'string') return step; @@ -66,7 +71,7 @@ function InstructionBlock({ block, open, onToggle }) { function DurationPart({ name, children }) { return ( {children} @@ -176,7 +181,13 @@ export function ClientOverviewPage({ const showPower = hasSubscription && Boolean(selectedTag); const canStart = Boolean(selectedTag || state?.configExists); const [now, setNow] = useState(Date.now()); - const [durationMode, setDurationMode] = useState('digital'); + const [durationMode, setDurationMode] = useState(() => { + try { + return localStorage.getItem(DURATION_MODE_STORAGE_KEY) === 'words' ? 'words' : 'digital'; + } catch { + return 'digital'; + } + }); const [editingSubscription, setEditingSubscription] = useState(!state?.hasSubscription); const [subscriptionValidation, setSubscriptionValidation] = useState({ url: '', status: 'idle' }); const [showIntro, setShowIntro] = useState(!hasSubscription); @@ -449,7 +460,15 @@ export function ClientOverviewPage({ } function toggleDurationMode() { - setDurationMode((mode) => mode === 'digital' ? 'words' : 'digital'); + setDurationMode((mode) => { + const nextMode = mode === 'digital' ? 'words' : 'digital'; + try { + localStorage.setItem(DURATION_MODE_STORAGE_KEY, nextMode); + } catch { + // The visual preference still works for this session. + } + return nextMode; + }); } return ( @@ -502,11 +521,10 @@ export function ClientOverviewPage({
{connected ? ( ) : (

@@ -651,22 +670,21 @@ export function ClientOverviewPage({

Ваша подписка