Remove dead Drop dashboard UI so Agent Shell frontend build passes.

Dead Drop chat stays in Infonet Terminal; Mesh Chat dms tab is Agent Shell only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
BigBodyCobain
2026-06-10 09:40:54 -06:00
parent b9b99c1fa8
commit 039a0f9d0c
+8 -798
View File
@@ -1492,778 +1492,7 @@ const MeshChat = React.memo(function MeshChat(props: MeshChatProps) {
</>
)}
{/* Dead Drop chat UI moved to Infonet Terminal → Messages */}
{false && !dashboardRestrictedTab && activeTab === 'dms' && (
<>
{/* Sub-nav: Contacts | Inbox | Muted | (back to contacts from chat) */}
<div className="flex items-center gap-1 px-3 py-1.5 border-b border-[var(--border-primary)]/30 shrink-0">
{dmView === 'chat' ? (
<>
<button
onClick={() => {
setDmView('contacts');
setSelectedContact('');
setDmMessages([]);
}}
className="text-[13px] font-mono text-[var(--text-muted)] hover:text-cyan-400 transition-colors"
>
&lt; BACK
</button>
<span className="text-sm font-mono text-cyan-400 ml-2 truncate">
{selectedContact.slice(0, 16)}
</span>
{(() => {
const c = contacts[selectedContact];
if (!c) return null;
const trust = getContactTrustSummary(c);
if (trust?.transparencyConflict) {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-red-500/40 text-red-300 bg-red-950/20">
HISTORY CONFLICT
</span>
);
}
if (trust?.state === 'continuity_broken') {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-red-500/40 text-red-300 bg-red-950/20">
CONTINUITY BROKEN
</span>
);
}
if (trust?.state === 'mismatch') {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-orange-500/40 text-orange-300 bg-orange-950/20">
PREKEY CHANGED
</span>
);
}
if (trust?.registryMismatch) {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-red-500/40 text-red-400 bg-red-950/20">
KEY MISMATCH
</span>
);
}
if (trust?.state === 'sas_verified') {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-green-500/40 text-green-400 bg-green-950/20">
SAS VERIFIED
</span>
);
}
if (trust?.state === 'invite_pinned') {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-emerald-500/40 text-emerald-300 bg-emerald-950/20">
INVITE PINNED
</span>
);
}
if (trust?.state === 'tofu_pinned') {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-amber-500/30 text-amber-300 bg-amber-950/10">
TOFU ONLY
</span>
);
}
return null;
})()}
{(() => {
const c = contacts[selectedContact];
if (!c) return null;
if (c.witness_count && c.witness_count > 0) {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-cyan-500/30 text-cyan-300 bg-cyan-950/10">
WITNESSED {c.witness_count}
</span>
);
}
return null;
})()}
{(() => {
const c = contacts[selectedContact];
if (!c) return null;
if (c.vouch_count && c.vouch_count > 0) {
return (
<span className="ml-2 text-[12px] font-mono px-1.5 py-0.5 border border-purple-500/30 text-purple-300 bg-purple-950/10">
VOUCHES {c.vouch_count}
</span>
);
}
return null;
})()}
<button
onClick={handleDmTrustPrimaryAction}
className="ml-auto text-[12px] font-mono px-2 py-0.5 border border-cyan-800/40 text-cyan-400/90 hover:text-cyan-300 hover:border-cyan-600/60 transition-colors"
>
{dmTrustPrimaryButtonLabel}
</button>
<button
onClick={() => handleVouch(selectedContact)}
className="ml-2 text-[12px] font-mono px-2 py-0.5 border border-purple-800/40 text-purple-400/90 hover:text-purple-300 hover:border-purple-600/60 transition-colors"
>
VOUCH
</button>
<button
onClick={() => void handleRefreshSelectedContact()}
disabled={dmMaintenanceBusy}
className="ml-2 text-[12px] font-mono px-2 py-0.5 border border-amber-800/40 text-amber-300/90 hover:text-amber-200 hover:border-amber-600/60 transition-colors disabled:opacity-40"
>
REFRESH
</button>
<button
onClick={() => void handleResetSelectedContact()}
disabled={dmMaintenanceBusy}
className="ml-2 text-[12px] font-mono px-2 py-0.5 border border-red-800/40 text-red-300/90 hover:text-red-200 hover:border-red-600/60 transition-colors disabled:opacity-40"
>
RESET
</button>
</>
) : (
<>
<button
onClick={() => setDmView('contacts')}
className={`text-[13px] font-mono px-2 py-0.5 transition-colors ${
dmView === 'contacts'
? 'text-cyan-400 bg-cyan-950/30'
: 'text-[var(--text-muted)] hover:text-gray-400'
}`}
>
CONTACTS
</button>
<button
onClick={() => setDmView('inbox')}
className={`text-[13px] font-mono px-2 py-0.5 transition-colors flex items-center gap-1 ${
dmView === 'inbox'
? 'text-cyan-400 bg-cyan-950/30'
: 'text-[var(--text-muted)] hover:text-gray-400'
}`}
>
INBOX
{accessRequests.length > 0 && (
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-[blink_1s_step-end_infinite]" />
)}
</button>
<button
onClick={() => setDmView('muted')}
className={`text-[13px] font-mono px-2 py-0.5 transition-colors flex items-center gap-1 ${
dmView === 'muted'
? 'text-cyan-400 bg-cyan-950/30'
: 'text-[var(--text-muted)] hover:text-gray-400'
}`}
>
<EyeOff size={8} />
MUTED
{mutedArray.length > 0 && (
<span className="text-[11px] text-[var(--text-muted)]">
({mutedArray.length})
</span>
)}
</button>
<button
onClick={() => setShowAddContact(!showAddContact)}
disabled={secureDmBlocked}
className="ml-auto p-1 hover:bg-[var(--hover-accent)] text-[var(--text-muted)] hover:text-cyan-400 transition-colors"
title="Request access"
>
<UserPlus size={11} />
</button>
</>
)}
</div>
{dmView === 'chat' && showSas && sasPhrase && (
<div className="px-3 pb-1 text-[13px] font-mono text-cyan-400/80 border-b border-[var(--border-primary)]/20">
SAS: <span className="text-cyan-300">{sasPhrase}</span>
{selectedContactInfo &&
selectedContactTrustSummary?.state === 'invite_pinned' && (
<div className="mt-1 text-[12px] font-mono text-emerald-300/90 leading-[1.65]">
This contact was anchored by an imported signed invite. SAS is still useful
as an extra continuity check.
</div>
)}
{selectedContactInfo &&
selectedContactTrustSummary?.state === 'tofu_pinned' && (
<div className="mt-1 text-[12px] font-mono text-amber-300/90 leading-[1.65]">
First contact is still TOFU-only. Compare this phrase out of band before
treating the sender as verified.
</div>
)}
{selectedContactInfo &&
selectedContactTrustSummary?.state !== 'sas_verified' &&
selectedContactTrustSummary?.state !== 'mismatch' &&
selectedContactTrustSummary?.state !== 'continuity_broken' &&
!selectedContactTrustSummary?.transparencyConflict && (
<div className="mt-2 flex items-center gap-1.5">
<input
value={sasConfirmInput}
onChange={(e) => setSasConfirmInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
void handleConfirmSelectedContactSas();
}
}}
placeholder="Type the phrase you both confirmed"
className="flex-1 min-w-0 bg-black/30 border border-cyan-900/30 px-2 py-1 text-[12px] font-mono text-cyan-100 placeholder:text-cyan-700/70 focus:outline-none focus:border-cyan-600/60"
/>
<button
onClick={() => void handleConfirmSelectedContactSas()}
disabled={dmMaintenanceBusy}
className="text-[12px] font-mono px-2 py-1 border border-emerald-800/40 text-emerald-300 hover:text-emerald-200 hover:border-emerald-600/60 transition-colors disabled:opacity-40"
>
CONFIRM SAS
</button>
</div>
)}
{selectedContactInfo &&
selectedContactTrustSummary?.state === 'continuity_broken' &&
selectedContactTrustSummary?.rootMismatch && (
<>
<div className="mt-1 text-[12px] font-mono text-red-300/90 leading-[1.65]">
{`${rootWitnessContinuityLabel(selectedContactTrustSummary)} changed for this contact.`}{' '}
Compare the SAS phrase for the newly observed root, then recover only if
the ceremony checks out.
</div>
<div className="mt-2 flex items-center gap-1.5">
<input
value={sasConfirmInput}
onChange={(e) => setSasConfirmInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
void handleRecoverSelectedContactRootContinuity();
}
}}
placeholder="Type the phrase you both confirmed for the new root"
className="flex-1 min-w-0 bg-black/30 border border-red-900/30 px-2 py-1 text-[12px] font-mono text-cyan-100 placeholder:text-red-700/70 focus:outline-none focus:border-red-600/60"
/>
<button
onClick={() => void handleRecoverSelectedContactRootContinuity()}
disabled={dmMaintenanceBusy}
className="text-[12px] font-mono px-2 py-1 border border-red-800/40 text-red-300 hover:text-red-200 hover:border-red-600/60 transition-colors disabled:opacity-40"
>
RECOVER ROOT
</button>
</div>
</>
)}
{selectedContactInfo?.remotePrekeyMismatch && (
<div className="mt-2 text-[12px] font-mono text-red-300/85 leading-[1.65]">
{selectedContactTrustSummary?.rootMismatch
? `${rootWitnessContinuityLabel(selectedContactTrustSummary)} changed. Recover only after you compare the new SAS phrase out of band.`
: 'Acknowledge the changed fingerprint first, then compare and confirm SAS again.'}
</div>
)}
</div>
)}
{activeTab === 'dms' && !secureDmBlocked && (
<div className="px-3 py-1.5 border-b border-[var(--border-primary)]/20 shrink-0 flex items-center gap-2">
<span
className={`text-[12px] font-mono px-1.5 py-0.5 border ${dmTransportStatus.className}`}
>
{dmTransportStatus.label}
</span>
<span className="text-[12px] font-mono text-[var(--text-muted)]">
{dmTransportMode === 'reticulum'
? 'Direct private delivery active.'
: dmTransportMode === 'hidden'
? 'Hidden transport active.'
: dmTransportMode === 'relay'
? 'Relay fallback active.'
: dmTransportMode === 'ready'
? 'Private lane ready.'
: 'Lower-trust mode.'}
</span>
</div>
)}
{activeTab === 'dms' && unresolvedSenderSealCount > 0 && (
<div className="px-3 py-2 border-b border-red-900/30 bg-red-950/18 text-red-300 leading-[1.65] shrink-0">
<div className="text-[13px] font-mono tracking-[0.18em] mb-1">
UNRESOLVED SEALED SENDERS
</div>
<div className="text-sm font-mono">
{unresolvedSenderSealCount} sealed-sender message
{unresolvedSenderSealCount === 1 ? '' : 's'} could not be mapped to a
trusted contact or verified sender key. Keep Wormhole reachable and refresh
contact trust before relying on them.
</div>
</div>
)}
{activeTab === 'dms' && dmView === 'chat' && dmTrustHint && selectedContactInfo && (
<div
className={`px-3 py-2 border-b leading-[1.65] shrink-0 ${
dmTrustHint.severity === 'danger'
? 'border-red-900/30 bg-red-950/20 text-red-300'
: 'border-amber-900/30 bg-amber-950/10 text-amber-200'
}`}
>
<div className="flex items-start gap-2">
<div className="flex-1 min-w-0">
<div className="text-[13px] font-mono tracking-[0.18em] mb-1">
{dmTrustHint.title}
</div>
<div className="text-sm font-mono">{dmTrustHint.detail}</div>
{selectedContactInfo.remotePrekeyMismatch && (
<div className="mt-2 text-[13px] font-mono text-red-200/85">
pinned {shortTrustFingerprint(selectedContactInfo.remotePrekeyFingerprint)} observed{' '}
{shortTrustFingerprint(selectedContactInfo.remotePrekeyObservedFingerprint)}
</div>
)}
{!selectedContactInfo.remotePrekeyMismatch &&
selectedContactInfo.remotePrekeyRootMismatch && (
<div className="mt-2 text-[13px] font-mono text-red-200/85">
pinned root {shortTrustFingerprint(selectedContactInfo.remotePrekeyRootFingerprint)}
observed root{' '}
{shortTrustFingerprint(selectedContactInfo.remotePrekeyObservedRootFingerprint)}
</div>
)}
{!selectedContactInfo.remotePrekeyMismatch &&
selectedContactTrustSummary?.state === 'tofu_pinned' &&
selectedContactInfo.remotePrekeyFingerprint && (
<div className="mt-2 text-[13px] font-mono text-amber-200/85">
first-sight pin {shortTrustFingerprint(selectedContactInfo.remotePrekeyFingerprint)}
verify before sensitive use
</div>
)}
{!selectedContactInfo.remotePrekeyMismatch &&
selectedContactTrustSummary?.state === 'invite_pinned' &&
(selectedContactInfo.invitePinnedTrustFingerprint ||
selectedContactInfo.remotePrekeyFingerprint) && (
<div className="mt-2 text-[13px] font-mono text-emerald-200/85">
invite pin{' '}
{shortTrustFingerprint(
selectedContactInfo.invitePinnedTrustFingerprint ||
selectedContactInfo.remotePrekeyFingerprint,
)}{' '}
{selectedContactTrustSummary?.rootAttested &&
(selectedContactInfo.invitePinnedRootFingerprint ||
selectedContactInfo.remotePrekeyRootFingerprint)
? ` ${rootWitnessBadgeLabel(selectedContactTrustSummary).toLowerCase()} ${shortTrustFingerprint(
selectedContactInfo.invitePinnedRootFingerprint ||
selectedContactInfo.remotePrekeyRootFingerprint,
)}`
: ''}{' '}
imported out of band before first contact
</div>
)}
{selectedContactTrustSummary?.state === 'continuity_broken' &&
selectedContactTrustSummary?.rootMismatch && (
<div className="mt-2 text-[13px] font-mono text-red-200/85 leading-[1.7]">
{`${rootWitnessContinuityLabel(selectedContactTrustSummary).toLowerCase()} broke for this contact.`}{' '}
Re-verify SAS or replace the signed invite before trusting the new
root.
</div>
)}
{selectedContactInfo.remotePrekeyTransparencyConflict && (
<div className="mt-2 text-[13px] font-mono text-red-200/85 leading-[1.7]">
prekey history conflict observed and trust stays degraded until you
explicitly acknowledge the changed fingerprint.
</div>
)}
{selectedContactInfo.remotePrekeyLookupMode === 'legacy_agent_id' && (
<div className="mt-2 text-[13px] font-mono text-yellow-200/85 leading-[1.7]">
bootstrap path: legacy direct agent ID lookup.
{selectedContactInfo.invitePinnedPrekeyLookupHandle
? ' Refresh from the signed invite to tighten lookup privacy.'
: ' Import or re-import a signed invite to avoid stable-ID lookup.'}
</div>
)}
{selectedContactInfo.remotePrekeyLookupMode === 'invite_lookup_handle' && (
<div className="mt-2 text-[13px] font-mono text-cyan-200/85 leading-[1.7]">
bootstrap path: invite-scoped lookup handle. Stable agent ID was not
required on the lookup path.
</div>
)}
{(selectedContactInfo.witness_count ?? 0) > 0 && (
<div className="mt-2 text-[13px] font-mono text-cyan-200/75 leading-[1.7]">
witness observations: {selectedContactInfo.witness_count}
{selectedContactInfo.witness_checked_at
? `, last seen ${timeAgo(
selectedContactInfo.witness_checked_at > 1_000_000_000_000
? selectedContactInfo.witness_checked_at
: selectedContactInfo.witness_checked_at * 1000,
)}`
: ''}
</div>
)}
</div>
<div className="flex items-center gap-1.5 shrink-0">
<button
onClick={handleDmTrustPrimaryAction}
className="text-[12px] font-mono px-2 py-0.5 border border-cyan-800/40 text-cyan-300 hover:text-cyan-200 hover:border-cyan-600/60 transition-colors"
>
{dmTrustPrimaryButtonLabel}
</button>
{selectedContactInfo.remotePrekeyMismatch &&
!selectedContactTrustSummary?.rootMismatch && (
<button
onClick={() => void handleTrustSelectedRemotePrekey()}
disabled={dmMaintenanceBusy}
className="text-[12px] font-mono px-2 py-0.5 border border-orange-700/40 text-orange-300 hover:text-orange-200 hover:border-orange-500/60 transition-colors disabled:opacity-40"
>
TRUST NEW KEY
</button>
)}
</div>
</div>
</div>
)}
{/* Add contact / request access form */}
<AnimatePresence>
{showAddContact && dmView !== 'chat' && !secureDmBlocked && (
<motion.div
initial={{ height: 0 }}
animate={{ height: 'auto' }}
exit={{ height: 0 }}
className="overflow-hidden border-b border-[var(--border-primary)]/30 shrink-0"
>
<div className="px-3 py-2 space-y-1.5">
<div className="text-[13px] font-mono text-[var(--text-muted)] leading-[1.65]">
Enter an Agent ID for a contact you already pinned with a signed invite
to request Dead Drop access. If you only have older local state, use
terminal <span className="text-yellow-400">dm add</span> only for
legacy migration.
</div>
<div className="flex items-center gap-1.5">
<input
value={addContactId}
onChange={(e) => setAddContactId(e.target.value)}
placeholder="!sb_a3f2c891..."
className="flex-1 bg-[var(--bg-secondary)]/50 border border-[var(--border-primary)] text-sm font-mono text-cyan-300 px-2 py-1 outline-none placeholder:text-[var(--text-muted)]"
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleRequestComposerAction();
}
}}
/>
<button
onClick={handleRequestComposerAction}
disabled={!addContactId.trim() || !hasId}
className="text-[13px] font-mono px-2 py-1 bg-cyan-900/20 text-cyan-400 hover:bg-cyan-800/30 disabled:opacity-30 transition-colors"
>
REQUEST
</button>
</div>
{pendingSent.includes(addContactId.trim()) && (
<div className="text-[13px] font-mono text-yellow-500/70">
Request already sent
</div>
)}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Content area */}
<div className="flex-1 overflow-y-auto styled-scrollbar px-3 py-1.5 space-y-0.5 border-l-2 border-cyan-800/25">
{secureDmBlocked && (
<div className="flex h-full min-h-[220px] items-center justify-center py-6">
<div className="max-w-sm w-full border border-cyan-900/30 bg-cyan-950/10 px-4 py-5 text-center">
<div className="inline-flex items-center justify-center w-10 h-10 border border-cyan-700/40 bg-black/30 text-cyan-300 mb-3">
<Lock size={16} />
</div>
<div className="text-sm font-mono tracking-[0.24em] text-cyan-300 mb-2">
DEAD DROP LOCKED
</div>
<div className="text-sm font-mono text-[var(--text-secondary)] leading-[1.7]">
Need Wormhole activated.
</div>
<div className="mt-2 text-[13px] font-mono text-cyan-300/70">
Contacts, inbox, and private messages unlock once the private lane is up.
</div>
</div>
</div>
)}
{/* CONTACTS VIEW */}
{!secureDmBlocked && dmView === 'contacts' && (
<>
{contactList.length === 0 && (
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
No contacts yet. Use <span className="text-cyan-500/70">+</span> to
request access.
</div>
)}
{contactList.map(([id, c]) => {
const trust = getContactTrustSummary(c);
return (
<div
key={id}
className="flex items-center gap-2 py-1.5 border-b border-[var(--border-primary)]/30 last:border-0 cursor-pointer hover:bg-[var(--bg-secondary)]/50 px-1 -mx-1 transition-colors"
onClick={() => openChat(id)}
>
<Lock size={10} className="text-[var(--text-muted)] shrink-0" />
<span className="text-sm font-mono text-cyan-300 truncate">
{c.alias || id.slice(0, 16)}
</span>
{c.remotePrekeyMismatch && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-orange-500/40 text-orange-300 bg-orange-950/20">
REVERIFY
</span>
)}
{!c.remotePrekeyMismatch && c.verify_mismatch && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-red-500/40 text-red-300 bg-red-950/20">
MISMATCH
</span>
)}
{!c.remotePrekeyMismatch && !c.verify_mismatch && trust?.state === 'invite_pinned' && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-emerald-500/40 text-emerald-300 bg-emerald-950/20">
INVITE PINNED
</span>
)}
{!c.remotePrekeyMismatch && !c.verify_mismatch && trust?.state === 'sas_verified' && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-green-500/40 text-green-400 bg-green-950/20">
SAS VERIFIED
</span>
)}
{!c.remotePrekeyMismatch &&
!c.verify_mismatch &&
!c.remotePrekeyTransparencyConflict &&
c.remotePrekeyLookupMode === 'legacy_agent_id' && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-yellow-500/30 text-yellow-300 bg-yellow-950/10">
LEGACY LOOKUP
</span>
)}
{!c.remotePrekeyMismatch && !c.verify_mismatch && c.remotePrekeyTransparencyConflict && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-red-500/40 text-red-300 bg-red-950/20">
HISTORY CONFLICT
</span>
)}
{!c.remotePrekeyMismatch &&
!c.verify_mismatch &&
trust?.state === 'tofu_pinned' && (
<span className="text-[11px] font-mono px-1.5 py-0.5 border border-amber-500/30 text-amber-300 bg-amber-950/10">
TOFU ONLY
</span>
)}
<button
onClick={(e) => {
e.stopPropagation();
handleBlockDM(id);
}}
className="ml-auto p-0.5 text-[var(--text-muted)] hover:text-red-400 hover:bg-red-900/20 transition-colors"
title="Block"
>
<Ban size={10} />
</button>
</div>
);
})}
{pendingSent.length > 0 && (
<>
<div className="text-[13px] font-mono text-[var(--text-muted)] mt-2 mb-1">
PENDING SENT
</div>
{pendingSent.map((id) => (
<div
key={id}
className="flex items-center gap-2 py-1 text-sm font-mono text-[var(--text-muted)]"
>
<span className="w-1.5 h-1.5 rounded-full bg-yellow-600/50" />
<span className="truncate">{id.slice(0, 16)}</span>
<span className="ml-auto text-[12px] text-[var(--text-muted)]">
awaiting
</span>
</div>
))}
</>
)}
</>
)}
{/* INBOX VIEW — access requests */}
{!secureDmBlocked && dmView === 'inbox' && (
<>
{accessRequests.length === 0 && (
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
No incoming requests
</div>
)}
{accessRequests.map((req) => {
const requestActionsAllowed = shouldAllowRequestActions(req);
const recoveryState = req.sender_recovery_state;
return (
<div
key={req.sender_id}
className="py-2 border-b border-[var(--border-primary)]/30 last:border-0"
>
<div className="flex items-center gap-1.5">
<UserPlus size={10} className="text-cyan-500 shrink-0" />
<span className="text-sm font-mono text-cyan-300 truncate">
{req.sender_id.slice(0, 16)}
</span>
{recoveryState === 'verified' && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-green-500/30 text-green-400 bg-green-950/20">
VERIFIED
</span>
)}
{recoveryState === 'pending' && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-yellow-500/30 text-yellow-300 bg-yellow-950/20">
RECOVERY PENDING
</span>
)}
{recoveryState === 'failed' && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-red-500/30 text-red-300 bg-red-950/20">
RECOVERY FAILED
</span>
)}
<span className="text-[12px] font-mono text-[var(--text-muted)] ml-auto shrink-0">
{timeAgo(req.timestamp)}
</span>
</div>
<div className="text-[13px] font-mono text-[var(--text-muted)] mt-0.5 leading-[1.65]">
Requesting Dead Drop access
</div>
{req.geo_hint && (
<div className="text-[12px] font-mono text-[var(--text-muted)] mt-0.5">
Geo hint (not proof): {req.geo_hint}
</div>
)}
{!requestActionsAllowed && (
<div className="text-[12px] font-mono text-yellow-300 mt-0.5 leading-[1.65]">
Sender authority is not verified yet. Actions stay disabled until
local recovery succeeds.
</div>
)}
<div className="flex items-center gap-1.5 mt-1.5">
<button
onClick={() => handleAcceptRequest(req.sender_id)}
disabled={!requestActionsAllowed}
className={`flex items-center gap-1 text-[13px] font-mono px-2 py-0.5 transition-colors ${
requestActionsAllowed
? 'bg-cyan-900/20 text-cyan-400 hover:bg-cyan-800/30'
: 'bg-cyan-950/10 text-cyan-700 cursor-not-allowed opacity-50'
}`}
>
<Check size={9} /> ACCEPT
</button>
<button
onClick={() => handleDenyRequest(req.sender_id)}
disabled={!requestActionsAllowed}
className={`flex items-center gap-1 text-[13px] font-mono px-2 py-0.5 transition-colors ${
requestActionsAllowed
? 'bg-gray-900/30 text-gray-400 hover:bg-gray-800/40'
: 'bg-gray-950/20 text-gray-600 cursor-not-allowed opacity-50'
}`}
>
<X size={9} /> DENY
</button>
<button
onClick={() => handleBlockDM(req.sender_id)}
disabled={!requestActionsAllowed}
className={`flex items-center gap-1 text-[13px] font-mono px-2 py-0.5 ml-auto transition-colors ${
requestActionsAllowed
? 'text-[var(--text-muted)] hover:text-red-400 hover:bg-red-900/20'
: 'text-[var(--text-muted)] opacity-50 cursor-not-allowed'
}`}
>
<Ban size={9} /> BLOCK
</button>
</div>
</div>
);
})}
</>
)}
{/* MUTED LIST VIEW */}
{!secureDmBlocked && dmView === 'muted' && (
<>
{mutedArray.length === 0 && (
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
No muted users
</div>
)}
{mutedArray.map((uid) => (
<div
key={uid}
className="flex items-center gap-2 py-1.5 border-b border-[var(--border-primary)]/30 last:border-0 px-1 -mx-1"
>
<EyeOff size={10} className="text-[var(--text-muted)] shrink-0" />
<span className="text-sm font-mono text-[var(--text-secondary)] truncate flex-1">
{uid.slice(0, 20)}
</span>
<button
onClick={() => handleUnmute(uid)}
className="flex items-center gap-1 text-[12px] font-mono px-2 py-0.5 bg-cyan-900/20 text-cyan-500 hover:bg-cyan-800/30 transition-colors"
>
<Eye size={8} /> UNMUTE
</button>
</div>
))}
</>
)}
{/* CHAT VIEW */}
{!secureDmBlocked && dmView === 'chat' && (
<>
{dmMessages.length === 0 && (
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
<Lock size={11} className="inline mr-1 mb-0.5" />
E2E encrypted dead drop no messages yet
</div>
)}
{dmMessages.map((m) => (
<div key={m.msg_id} className="py-0.5 leading-[1.65]">
<div className="flex gap-1.5 text-sm font-mono">
<span
className={`shrink-0 ${
m.sender_id === identity?.nodeId
? 'text-cyan-500'
: 'text-cyan-400'
}`}
>
{m.sender_id === identity?.nodeId
? 'you'
: m.sender_id.slice(0, 12)}
</span>
{m.sender_id !== identity?.nodeId && m.seal_verified === true && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-green-500/30 text-green-400 bg-green-950/20">
VERIFIED
</span>
)}
{m.sender_id !== identity?.nodeId && m.seal_resolution_failed && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-red-500/30 text-red-300 bg-red-950/20">
SEAL UNRESOLVED
</span>
)}
{m.sender_id !== identity?.nodeId &&
!m.seal_resolution_failed &&
m.seal_verified === false && (
<span className="text-[12px] font-mono px-1.5 py-0.5 border border-red-500/30 text-red-400 bg-red-950/20">
UNVERIFIED
</span>
)}
{m.transport && (
<span
className={`text-[12px] font-mono px-1.5 py-0.5 border ${
m.transport === 'reticulum'
? 'border-green-500/30 text-green-400 bg-green-950/20'
: 'border-yellow-500/30 text-yellow-400 bg-yellow-950/20'
}`}
>
{m.transport === 'reticulum' ? 'DIRECT' : 'RELAY'}
</span>
)}
<span className="text-[var(--text-secondary)] break-words whitespace-pre-wrap flex-1">
{m.plaintext || '[encrypted]'}
</span>
<span className="text-[var(--text-muted)] shrink-0 text-[13px]">
{timeAgo(m.timestamp)}
</span>
</div>
</div>
))}
</>
)}
<div ref={messagesEndRef} />
</div>
</>
)}
{/* Dead Drop chat UI: Infonet Terminal → Messages */}
</div>
{/* INPUT BAR */}
@@ -2341,21 +1570,15 @@ const MeshChat = React.memo(function MeshChat(props: MeshChatProps) {
? privateInfonetReady
? `→ INFONET${selectedGate ? ` / ${selectedGate}` : ''}${privateInfonetTransportReady ? '' : ' / EXPERIMENTAL ENCRYPTION'}`
: '→ PRIVATE LANE LOCKED'
: activeTab === 'meshtastic'
? canUsePublicMeshInput
? meshDirectTarget
? `→ MESH / TO ${meshDirectTarget.toUpperCase()} / FROM ${activePublicMeshAddress.toUpperCase()}`
: `→ MESH / ${meshRegion} / ${meshChannel} / ${activePublicMeshAddress.toUpperCase()}`
: publicMeshBlockedByWormhole
? '→ MESH BLOCKED / WORMHOLE ACTIVE'
: canUsePublicMeshInput
? meshDirectTarget
? `→ MESH / TO ${meshDirectTarget.toUpperCase()} / FROM ${activePublicMeshAddress.toUpperCase()}`
: `→ MESH / ${meshRegion} / ${meshChannel} / ${activePublicMeshAddress.toUpperCase()}`
: publicMeshBlockedByWormhole
? '→ MESH BLOCKED / WORMHOLE ACTIVE'
: hasStoredPublicLaneIdentity
? '→ MESH OFF'
: '→ MESH LOCKED'
: activeTab === 'dms' && secureDmBlocked
? '→ DEAD DROP LOCKED'
: dmView === 'chat' && selectedContact
? `→ DEAD DROP / ${selectedContact.slice(0, 14)}`
: '→ SELECT TARGET'}
: '→ MESH LOCKED'}
</span>
)}
</div>
@@ -2386,19 +1609,6 @@ const MeshChat = React.memo(function MeshChat(props: MeshChatProps) {
OPEN PRIVATE LANE BRIEF
</span>
</button>
) : activeTab === 'dms' && secureDmBlocked ? (
<button
onClick={() => setDeadDropUnlockOpen(true)}
className="w-full flex items-center justify-between gap-2 px-3 py-2 border border-cyan-700/40 bg-cyan-950/15 text-cyan-300 hover:bg-cyan-950/25 hover:border-cyan-500/50 transition-colors"
>
<span className="inline-flex items-center gap-2 text-sm font-mono tracking-[0.2em]">
<Lock size={11} />
UNLOCK DEAD DROP
</span>
<span className="text-[12px] font-mono text-cyan-300/70">
NEED WORMHOLE
</span>
</button>
) : activeTab === 'meshtastic' && !canUsePublicMeshInput ? (
<button
onClick={handleMeshActivationAction}