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({
Ваша подписка