diff --git a/webapp/src/components/SendsPage.tsx b/webapp/src/components/SendsPage.tsx index 08c1885..09ec156 100644 --- a/webapp/src/components/SendsPage.tsx +++ b/webapp/src/components/SendsPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'preact/hooks'; +import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { CheckCheck, ChevronLeft, Copy, Eye, EyeOff, File, FileText, LayoutGrid, Pencil, Plus, RefreshCw, Save, Send as SendIcon, Trash2, X } from 'lucide-preact'; import { copyTextToClipboard } from '@/lib/clipboard'; import type { Send, SendDraft } from '@/lib/types'; @@ -79,6 +79,7 @@ export default function SendsPage(props: SendsPageProps) { const [isMobileLayout, setIsMobileLayout] = useState(getInitialIsMobileLayout); const [mobilePanel, setMobilePanel] = useState<'list' | 'detail' | 'edit'>('list'); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + const mobileSidebarToggleKeyRef = useRef(props.mobileSidebarToggleKey); const [autoCopyLink, setAutoCopyLink] = useState(() => { try { return localStorage.getItem(AUTO_COPY_KEY) === '1'; @@ -108,7 +109,8 @@ export default function SendsPage(props: SendsPageProps) { }, []); useEffect(() => { - if (!props.mobileSidebarToggleKey) return; + if (props.mobileSidebarToggleKey === mobileSidebarToggleKeyRef.current) return; + mobileSidebarToggleKeyRef.current = props.mobileSidebarToggleKey; setMobileSidebarOpen((open) => !open); }, [props.mobileSidebarToggleKey]); diff --git a/webapp/src/components/SettingsPage.tsx b/webapp/src/components/SettingsPage.tsx index 83813fb..378fae7 100644 --- a/webapp/src/components/SettingsPage.tsx +++ b/webapp/src/components/SettingsPage.tsx @@ -269,7 +269,33 @@ export default function SettingsPage(props: SettingsPageProps) {
-
diff --git a/webapp/src/components/VaultPage.tsx b/webapp/src/components/VaultPage.tsx index f0ddfa3..efc7bf6 100644 --- a/webapp/src/components/VaultPage.tsx +++ b/webapp/src/components/VaultPage.tsx @@ -127,6 +127,7 @@ export default function VaultPage(props: VaultPageProps) { const folderSortMenuRef = useRef(null); const attachmentInputRef = useRef(null); const listPanelRef = useRef(null); + const mobileSidebarToggleKeyRef = useRef(props.mobileSidebarToggleKey); const suppressNextSortScrollRef = useRef(false); const sshSeedTicketRef = useRef(0); const sshFingerprintTicketRef = useRef(0); @@ -147,7 +148,8 @@ export default function VaultPage(props: VaultPageProps) { }, []); useEffect(() => { - if (!props.mobileSidebarToggleKey) return; + if (props.mobileSidebarToggleKey === mobileSidebarToggleKeyRef.current) return; + mobileSidebarToggleKeyRef.current = props.mobileSidebarToggleKey; setMobileSidebarOpen((open) => !open); }, [props.mobileSidebarToggleKey]); diff --git a/webapp/src/styles/dark.css b/webapp/src/styles/dark.css index 04b7884..9538d50 100644 --- a/webapp/src/styles/dark.css +++ b/webapp/src/styles/dark.css @@ -166,10 +166,23 @@ -webkit-backdrop-filter: blur(12px); } +:root[data-theme='dark'] .user-chip { + background: color-mix(in srgb, var(--panel) 86%, transparent); + border-color: var(--line); + color: var(--text); + box-shadow: var(--shadow-sm); +} + +:root[data-theme='dark'] .user-chip:hover { + background: var(--panel-subtle); + border-color: color-mix(in srgb, var(--primary) 24%, var(--line)); +} + /* ── dark mode depth ── */ :root[data-theme='dark'] .card, :root[data-theme='dark'] .list-panel, -:root[data-theme='dark'] .sidebar-block { +:root[data-theme='dark'] .sidebar-block, +:root[data-theme='dark'] .mobile-sidebar-sheet { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.20), 0 8px 24px rgba(0, 0, 0, 0.16); } @@ -181,6 +194,105 @@ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.24), 0 0 0 1px rgba(139, 184, 255, 0.12); } -:root[data-theme='dark'] .list-item.active { - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), inset 4px 0 0 rgba(139, 184, 255, 0.70); +:root[data-theme='dark'] .mobile-sidebar-sheet, +:root[data-theme='dark'] .mobile-sidebar-close, +:root[data-theme='dark'] .table tr, +:root[data-theme='dark'] .settings-subcard, +:root[data-theme='dark'] .import-summary-table-wrap, +:root[data-theme='dark'] .backup-help-bubble, +:root[data-theme='dark'] .backup-recommendation-card, +:root[data-theme='dark'] .backup-recommendation-dav-item, +:root[data-theme='dark'] .backup-browser-path, +:root[data-theme='dark'] .backup-browser-list, +:root[data-theme='dark'] .restore-progress-card, +:root[data-theme='dark'] .restore-progress-current, +:root[data-theme='dark'] .restore-progress-elapsed { + background: var(--panel); + border-color: var(--line); + color: var(--text); +} + +:root[data-theme='dark'] .mobile-sidebar-title, +:root[data-theme='dark'] .import-summary-close, +:root[data-theme='dark'] .backup-recommendation-group-title, +:root[data-theme='dark'] .backup-browser-path strong, +:root[data-theme='dark'] .restore-progress-current strong, +:root[data-theme='dark'] .custom-field-check span, +:root[data-theme='dark'] .notes { + color: var(--text); +} + +:root[data-theme='dark'] .backup-help-bubble::before { + background: var(--panel); + border-color: var(--line); +} + +:root[data-theme='dark'] .mobile-sidebar-close:hover, +:root[data-theme='dark'] .mobile-sidebar-sheet .tree-btn.active, +:root[data-theme='dark'] .mobile-settings-link.active, +:root[data-theme='dark'] .backup-destination-item.active, +:root[data-theme='dark'] .backup-interval-preset.active { + background: color-mix(in srgb, var(--primary) 14%, var(--panel)); + color: var(--primary-strong); +} + +:root[data-theme='dark'] .table td, +:root[data-theme='dark'] .attachment-row, +:root[data-theme='dark'] .custom-field-card, +:root[data-theme='dark'] .kv-line, +:root[data-theme='dark'] .kv-row, +:root[data-theme='dark'] .import-summary-table th, +:root[data-theme='dark'] .import-summary-table td, +:root[data-theme='dark'] .restore-progress-card, +:root[data-theme='dark'] .restore-progress-current, +:root[data-theme='dark'] .restore-progress-elapsed { + border-color: var(--line-soft); +} + +:root[data-theme='dark'] .import-summary-table th { + background: var(--panel-muted); + color: var(--muted-strong); +} + +:root[data-theme='dark'] .import-summary-failed-list { + background: color-mix(in srgb, var(--danger) 12%, var(--panel)); + border-color: color-mix(in srgb, var(--danger) 34%, var(--line)); + color: var(--danger); +} + +:root[data-theme='dark'] .backup-help-trigger, +:root[data-theme='dark'] .backup-destination-type, +:root[data-theme='dark'] .backup-interval-preset, +:root[data-theme='dark'] .restore-progress-meter { + background: var(--panel-muted); + border-color: var(--line); + color: var(--muted-strong); +} + +:root[data-theme='dark'] .backup-destination-item:hover, +:root[data-theme='dark'] .backup-interval-preset:hover:not(:disabled) { + background: var(--panel-subtle); + border-color: color-mix(in srgb, var(--primary) 34%, var(--line)); + color: var(--primary-strong); +} + +:root[data-theme='dark'] .backup-help-bubble, +:root[data-theme='dark'] .backup-recommendation-step, +:root[data-theme='dark'] .backup-recommendation-inline-note, +:root[data-theme='dark'] .backup-recommendation-linked-item, +:root[data-theme='dark'] .backup-browser-meta, +:root[data-theme='dark'] .backup-browser-empty, +:root[data-theme='dark'] .backup-inline-note, +:root[data-theme='dark'] .restore-progress-kicker, +:root[data-theme='dark'] .restore-progress-subtitle, +:root[data-theme='dark'] .restore-progress-current p, +:root[data-theme='dark'] .restore-progress-item, +:root[data-theme='dark'] .check-line { + color: var(--muted); +} + +:root[data-theme='dark'] .restore-progress-overlay { + background: var(--overlay-strong); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); } diff --git a/webapp/src/styles/management.css b/webapp/src/styles/management.css index 2db58e0..1dc3e91 100644 --- a/webapp/src/styles/management.css +++ b/webapp/src/styles/management.css @@ -392,6 +392,27 @@ @apply h-[180px] w-[180px] rounded-lg bg-white; } +.totp-secret-input-wrap { + @apply relative; +} + +.totp-secret-input { + padding-right: 84px; +} + +.totp-secret-actions { + @apply absolute right-2 top-1/2 inline-flex items-center gap-1; + transform: translateY(-50%); +} + +.totp-secret-icon-btn { + @apply h-8 w-8 min-w-8 gap-0 rounded-lg p-0; +} + +.totp-secret-icon-btn .btn-icon { + @apply m-0; +} + .section-head { @apply mb-2.5 flex items-center justify-between; }