+ {/* Enable/Disable Toggle */}
+
+
+
+
+ {t('Allow other devices to sync with this client')}
+
+
+
+
+
+ {/* Status Indicator */}
+ {isEnabled && (
+
+
+ {isConnected ? (
+
+ ) : (
+
+ )}
+
+ {isConnected ? t('Connected') : t('Connecting...')}
+
+
+ {activeSessions > 0 && (
+
+
+
+ {activeSessions} {t('active session(s)')}
+
+
+ )}
+
+ )}
+
+ {/* Rendezvous Relay */}
+
+
+
setRendezvousUrl(e.target.value)}
+ placeholder="wss://relay.example.com"
+ disabled={isEnabled}
+ />
+ {isEnabled && (
+
+ {t('Disable NRC to change the relay')}
+
+ )}
+
+
+ {/* Connections List */}
+
+
+
+
+
+
+ {connections.length === 0 ? (
+
+ {t('No devices connected yet')}
+
+ ) : (
+
+ {connections.map((connection) => (
+
+
+
{connection.label}
+
+ {new Date(connection.createdAt).toLocaleDateString()}
+ {connection.useCat && (
+
+ CAT
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ {t('Remove Device?')}
+
+ {t('This will revoke access for "{{label}}". The device will no longer be able to sync.', {
+ label: connection.label
+ })}
+
+
+
+ {t('Cancel')}
+ handleRemoveConnection(connection.id)}
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ >
+ {t('Remove')}
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Add Connection Dialog */}
+
+
+ {/* QR Code Dialog */}
+
+
+ )
+}
diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx
index bdb51066..9b771526 100644
--- a/src/components/Settings/index.tsx
+++ b/src/components/Settings/index.tsx
@@ -6,6 +6,7 @@ import EmojiPackList from '@/components/EmojiPackList'
import EmojiPickerDialog from '@/components/EmojiPickerDialog'
import FavoriteRelaysSetting from '@/components/FavoriteRelaysSetting'
import MailboxSetting from '@/components/MailboxSetting'
+import NRCSettings from '@/components/NRCSettings'
import NoteList from '@/components/NoteList'
import Tabs from '@/components/Tabs'
import {
@@ -73,6 +74,7 @@ import {
PencilLine,
RotateCcw,
ScanLine,
+ RefreshCw,
Server,
Settings2,
Smile,
@@ -105,7 +107,7 @@ const NOTIFICATION_STYLES = [
] as const
// Accordion item values for keyboard navigation
-const ACCORDION_ITEMS = ['general', 'appearance', 'relays', 'wallet', 'posts', 'emoji-packs', 'messaging', 'system']
+const ACCORDION_ITEMS = ['general', 'appearance', 'relays', 'sync', 'wallet', 'posts', 'emoji-packs', 'messaging', 'system']
export default function Settings() {
const { t, i18n } = useTranslation()
@@ -123,7 +125,7 @@ export default function Settings() {
// Get the visible accordion items based on pubkey availability
const visibleAccordionItems = pubkey
? ACCORDION_ITEMS
- : ACCORDION_ITEMS.filter((item) => !['wallet', 'posts', 'emoji-packs', 'messaging'].includes(item))
+ : ACCORDION_ITEMS.filter((item) => !['sync', 'wallet', 'posts', 'emoji-packs', 'messaging'].includes(item))
// Register as a navigation region - Settings decides what "up/down" means
const handleSettingsIntent = useCallback(
@@ -548,6 +550,23 @@ export default function Settings() {
+ {/* Sync (NRC) */}
+ {!!pubkey && (
+