Browse Source

bug-fixes

Nostr-Signature: 5c4b680a04363718d8de6aa05b824d30417221a9095be57bb9a7c2cf01c5af59 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 51ffa554e83a6a3c4ca97cffc7eca67e770ca822e43e9e78692bafcd63401c4df84e1fe030592e63982b509d3cfa8bfbd57c6b4257661b0f43adedef335c7575
main
Silberengel 2 weeks ago
parent
commit
34ae76bffe
  1. 1
      nostr/commit-signatures.jsonl
  2. 94
      src/app.html
  3. 15
      src/lib/components/CommentRenderer.svelte
  4. 15
      src/lib/utils/nostr-links.ts
  5. 230
      src/routes/users/[npub]/+page.svelte

1
nostr/commit-signatures.jsonl

@ -126,3 +126,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772298906,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"6aa4dcd1b3d8a933710a6eb43321aa4faaba56598c735a634069c882c83b4f03","sig":"80ce253e890e8e84c8138e004bc2aaea402379d9aa67f62793ac7a4b344de6a7223f46fc733b240215a983a3a9b574ea8d0858a184f06df58ee66212ba58ee53"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772298906,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"6aa4dcd1b3d8a933710a6eb43321aa4faaba56598c735a634069c882c83b4f03","sig":"80ce253e890e8e84c8138e004bc2aaea402379d9aa67f62793ac7a4b344de6a7223f46fc733b240215a983a3a9b574ea8d0858a184f06df58ee66212ba58ee53"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772299137,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","more-muted replyt-to"]],"content":"Signed commit: more-muted replyt-to","id":"fc0a91b526083b640d8116592fcac064fcf3cec9625b48dbd41c3877b2fe5444","sig":"998273d70d827ffbb939b4c149ff88e11c9f3aae3c5ddee78d860710f7fbff42c5ceed9433367b530bdc2869f9d382eb449537f813cf745c49f1a87a36926502"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772299137,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","more-muted replyt-to"]],"content":"Signed commit: more-muted replyt-to","id":"fc0a91b526083b640d8116592fcac064fcf3cec9625b48dbd41c3877b2fe5444","sig":"998273d70d827ffbb939b4c149ff88e11c9f3aae3c5ddee78d860710f7fbff42c5ceed9433367b530bdc2869f9d382eb449537f813cf745c49f1a87a36926502"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772299733,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","make relay timeouts more efficient\nsuppress diff on initial commit"]],"content":"Signed commit: make relay timeouts more efficient\nsuppress diff on initial commit","id":"5fbc2dfb13acab011df5a394a022267e69bbe908e696c4389e0c07ba83d58a0d","sig":"daf46d563c413e2481be2cbd2b00d3015cf601e19fe0a191ffbb18c2c07508b17e34ebda5c903a1391914f991cecd7a7a4e809fcba45e1f14ebab674117eb53c"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772299733,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","make relay timeouts more efficient\nsuppress diff on initial commit"]],"content":"Signed commit: make relay timeouts more efficient\nsuppress diff on initial commit","id":"5fbc2dfb13acab011df5a394a022267e69bbe908e696c4389e0c07ba83d58a0d","sig":"daf46d563c413e2481be2cbd2b00d3015cf601e19fe0a191ffbb18c2c07508b17e34ebda5c903a1391914f991cecd7a7a4e809fcba45e1f14ebab674117eb53c"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772300935,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"3618c494d594408165ebf461e290676817ab6cc8b0b076ccc02b35a487ae8da1","sig":"d9106ce1318e703df1c366835be69c0cab12fba3a9be0fed944b17e55fd3b44f3fc9d45b32366d1d237452f099ab3b07f8ad6199660972ce571ec23ae264e873"}

94
src/app.html

@ -39,6 +39,100 @@
} }
// Other unhandled rejections will still be logged by the browser // Other unhandled rejections will still be logged by the browser
}); });
// Suppress WebSocket connection errors for nostr relays
// These errors occur when relays are down or unreachable, which is expected behavior
// SimplePool from nostr-tools will retry connections, causing repeated console errors
(function() {
// List of known nostr relay URLs to suppress errors for
const nostrRelayPatterns = [
'wss://orly-relay.imwald.eu',
'wss://nostr.sovbit.host',
'wss://nostr21.com',
'wss://theforest.nostr1.com',
'wss://nostr.land',
'wss://relay.damus.io',
'wss://thecitadel.nostr1.com',
'wss://freelay.sovbit.host',
'wss://bevos.nostr1.com',
'wss://relay.primal.net',
'wss://nostr.mom',
'wss://relay.snort.social',
'wss://aggr.nostr.land'
];
// Helper function to check if an error message is a nostr relay connection error
function isNostrRelayConnectionError(message) {
if (typeof message !== 'string') return false;
return nostrRelayPatterns.some(pattern =>
message.includes(pattern) && (
message.includes('Verbindung') || // German: "connection"
message.includes('connection') ||
message.includes('kann keine Verbindung') || // German: "cannot establish connection"
message.includes('cannot establish') ||
message.includes('WebSocket') ||
message.includes('wss://') ||
message.includes('aufbauen') // German: "establish"
)
);
}
// Store original console methods
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;
// Override console.error to filter out WebSocket connection errors for nostr relays
console.error = function(...args) {
const message = args.join(' ');
// Suppress nostr relay connection errors (they're expected when relays are down)
if (isNostrRelayConnectionError(message)) {
// Optionally log at debug level instead of error level
if (typeof console.debug === 'function') {
console.debug('[Nostr Relay] Connection failed (expected):', ...args);
}
return; // Don't log the error
}
// For all other errors, use the original console.error
originalConsoleError.apply(console, args);
};
// Also override console.warn for consistency
console.warn = function(...args) {
const message = args.join(' ');
// Suppress nostr relay connection warnings too
if (isNostrRelayConnectionError(message)) {
if (typeof console.debug === 'function') {
console.debug('[Nostr Relay] Connection warning (expected):', ...args);
}
return;
}
originalConsoleWarn.apply(console, args);
};
// Handle window.onerror for global error catching
const originalOnError = window.onerror;
window.onerror = function(message, source, lineno, colno, error) {
const errorMessage = String(message || '');
// Suppress nostr relay connection errors
if (isNostrRelayConnectionError(errorMessage)) {
// Return true to prevent default error handling
return true;
}
// For other errors, call original handler if it exists
if (originalOnError) {
return originalOnError.call(this, message, source, lineno, colno, error);
}
return false;
};
})();
</script> </script>
%sveltekit.head% %sveltekit.head%
</head> </head>

15
src/lib/components/CommentRenderer.svelte

@ -15,7 +15,7 @@
import EventCopyButton from '$lib/components/EventCopyButton.svelte'; import EventCopyButton from '$lib/components/EventCopyButton.svelte';
import { import {
processContentWithNostrLinks, processContentWithNostrLinks,
getReferencedEventFromDiscussion, getReferencedEventWithTagType,
formatDiscussionTime, formatDiscussionTime,
type ProcessedContentPart type ProcessedContentPart
} from '$lib/utils/nostr-links.js'; } from '$lib/utils/nostr-links.js';
@ -42,9 +42,16 @@
nested = false nested = false
}: Props = $props(); }: Props = $props();
const referencedEvent = $derived(commentEvent const referencedEventWithTag = $derived(commentEvent
? getReferencedEventFromDiscussion(commentEvent, eventCache) ? getReferencedEventWithTagType(commentEvent, eventCache)
: undefined); : undefined);
const referencedEvent = $derived(referencedEventWithTag?.event);
const referencedTagType = $derived(referencedEventWithTag?.tagType);
const referencedLabel = $derived(
referencedTagType === 'q' ? 'Quoting:' : 'Reply-To:'
);
const contentParts = $derived(processContentWithNostrLinks(comment.content, eventCache, profileCache)); const contentParts = $derived(processContentWithNostrLinks(comment.content, eventCache, profileCache));
</script> </script>
@ -68,7 +75,7 @@
{#if referencedEvent} {#if referencedEvent}
<div class="referenced-event"> <div class="referenced-event">
<div class="referenced-event-header"> <div class="referenced-event-header">
<span class="referenced-event-label">Replying to:</span> <span class="referenced-event-label">{referencedLabel}</span>
<UserBadge pubkey={referencedEvent.pubkey} disableLink={true} inline={true} /> <UserBadge pubkey={referencedEvent.pubkey} disableLink={true} inline={true} />
<span class="referenced-event-time">{formatDiscussionTime(referencedEvent.created_at)}</span> <span class="referenced-event-time">{formatDiscussionTime(referencedEvent.created_at)}</span>
</div> </div>

15
src/lib/utils/nostr-links.ts

@ -381,11 +381,18 @@ export function getReferencedEventFromDiscussion(
event: NostrEvent, event: NostrEvent,
eventCache: Map<string, NostrEvent> eventCache: Map<string, NostrEvent>
): NostrEvent | undefined { ): NostrEvent | undefined {
return getReferencedEventWithTagType(event, eventCache)?.event;
}
export function getReferencedEventWithTagType(
event: NostrEvent,
eventCache: Map<string, NostrEvent>
): { event: NostrEvent; tagType: 'e' | 'a' | 'q' } | undefined {
// Check e-tag // Check e-tag
const eTag = event.tags.find(t => t[0] === 'e' && t[1])?.[1]; const eTag = event.tags.find(t => t[0] === 'e' && t[1])?.[1];
if (eTag) { if (eTag) {
const referenced = eventCache.get(eTag); const referenced = eventCache.get(eTag);
if (referenced) return referenced; if (referenced) return { event: referenced, tagType: 'e' };
} }
// Check a-tag // Check a-tag
@ -396,18 +403,20 @@ export function getReferencedEventFromDiscussion(
const kind = parseInt(parts[0]); const kind = parseInt(parts[0]);
const pubkey = parts[1]; const pubkey = parts[1];
const dTag = parts[2]; const dTag = parts[2];
return Array.from(eventCache.values()).find(e => const referenced = Array.from(eventCache.values()).find(e =>
e.kind === kind && e.kind === kind &&
e.pubkey === pubkey && e.pubkey === pubkey &&
e.tags.find(t => t[0] === 'd' && t[1] === dTag) e.tags.find(t => t[0] === 'd' && t[1] === dTag)
); );
if (referenced) return { event: referenced, tagType: 'a' };
} }
} }
// Check q-tag // Check q-tag
const qTag = event.tags.find(t => t[0] === 'q' && t[1])?.[1]; const qTag = event.tags.find(t => t[0] === 'q' && t[1])?.[1];
if (qTag) { if (qTag) {
return eventCache.get(qTag); const referenced = eventCache.get(qTag);
if (referenced) return { event: referenced, tagType: 'q' };
} }
return undefined; return undefined;

230
src/routes/users/[npub]/+page.svelte

@ -15,6 +15,7 @@
import { combineRelays } from '$lib/config.js'; import { combineRelays } from '$lib/config.js';
import { KIND, isEphemeralKind, isReplaceableKind } from '$lib/types/nostr.js'; import { KIND, isEphemeralKind, isReplaceableKind } from '$lib/types/nostr.js';
import { hasUnlimitedAccess } from '$lib/utils/user-access.js'; import { hasUnlimitedAccess } from '$lib/utils/user-access.js';
import { eventCache } from '$lib/services/nostr/event-cache.js';
const npub = ($page.params as { npub?: string }).npub || ''; const npub = ($page.params as { npub?: string }).npub || '';
@ -60,6 +61,48 @@
return quotedEvents.find(e => e.id === eventId); return quotedEvents.find(e => e.id === eventId);
}; };
// Helper to get author name from pubkey
function getAuthorName(pubkey: string): string {
// Try to find profile event in nostrLinkEvents cache
// Check both by profile key and by iterating values
const profileByKey = nostrLinkEvents.get(`profile:${pubkey}`);
let profileEvent = profileByKey || Array.from(nostrLinkEvents.values()).find(
e => e.kind === 0 && e.pubkey === pubkey
);
// If not found in nostrLinkEvents, try global eventCache
if (!profileEvent) {
try {
const cachedProfile = eventCache.getProfile(pubkey);
if (cachedProfile) {
profileEvent = cachedProfile;
}
} catch {
// eventCache not available, continue
}
}
if (profileEvent) {
try {
const profile = JSON.parse(profileEvent.content);
const name = profile.display_name || profile.name;
if (name && name.trim()) return name.trim();
} catch {
// Try tags if JSON parse fails
const nameTag = profileEvent.tags.find(t => t[0] === 'name' || t[0] === 'display_name')?.[1];
if (nameTag && nameTag.trim()) return nameTag.trim();
}
}
// Fallback to shortened pubkey
try {
const npub = nip19.npubEncode(pubkey);
return npub.slice(0, 12) + '...';
} catch {
return pubkey.slice(0, 8) + '...';
}
}
// Referenced events cache for activity (a-tags and e-tags) - use array for better reactivity // Referenced events cache for activity (a-tags and e-tags) - use array for better reactivity
let referencedEvents = $state<NostrEvent[]>([]); let referencedEvents = $state<NostrEvent[]>([]);
@ -163,6 +206,7 @@
} }
// Fetch events // Fetch events
const loadedEvents: NostrEvent[] = [];
if (eventIds.length > 0) { if (eventIds.length > 0) {
try { try {
const events = await Promise.race([ const events = await Promise.race([
@ -172,6 +216,7 @@
for (const event of events) { for (const event of events) {
nostrLinkEvents.set(event.id, event); nostrLinkEvents.set(event.id, event);
loadedEvents.push(event);
} }
} catch { } catch {
// Ignore fetch errors // Ignore fetch errors
@ -194,6 +239,7 @@
if (events.length > 0) { if (events.length > 0) {
nostrLinkEvents.set(events[0].id, events[0]); nostrLinkEvents.set(events[0].id, events[0]);
loadedEvents.push(events[0]);
} }
} catch { } catch {
// Ignore fetch errors // Ignore fetch errors
@ -201,6 +247,38 @@
} }
} }
} }
// Load profiles for authors of loaded events
if (loadedEvents.length > 0) {
const authorPubkeys = new Set<string>();
for (const event of loadedEvents) {
if (event.pubkey) {
authorPubkeys.add(event.pubkey);
}
}
// Fetch profiles for all authors
if (authorPubkeys.size > 0) {
try {
const profiles = await Promise.race([
nostrClient.fetchEvents([{ kinds: [0], authors: Array.from(authorPubkeys), limit: authorPubkeys.size }]),
new Promise<NostrEvent[]>((resolve) => setTimeout(() => resolve([]), 10000))
]);
// Store profiles in cache (use a special key format: `profile:${pubkey}`)
for (const profile of profiles) {
// Store with a key that includes the pubkey so we can find it
nostrLinkEvents.set(`profile:${profile.pubkey}`, profile);
// Also store by ID if it has one
if (profile.id) {
nostrLinkEvents.set(profile.id, profile);
}
}
} catch {
// Ignore profile fetch errors
}
}
}
} }
// Get event from nostr: link // Get event from nostr: link
@ -1925,7 +2003,8 @@ i *
{#if quotedEvent} {#if quotedEvent}
<div class="quoted-event"> <div class="quoted-event">
<div class="quoted-event-header"> <div class="quoted-event-header">
<UserBadge pubkey={quotedEvent.pubkey} disableLink={true} /> <span class="quoted-event-label">Quoting:</span>
<UserBadge pubkey={quotedEvent.pubkey} disableLink={true} inline={true} />
<span class="quoted-event-time">{formatMessageTime(quotedEvent.created_at)}</span> <span class="quoted-event-time">{formatMessageTime(quotedEvent.created_at)}</span>
</div> </div>
<div class="quoted-event-content">{quotedEvent.content || '(No content)'}</div> <div class="quoted-event-content">{quotedEvent.content || '(No content)'}</div>
@ -1984,7 +2063,7 @@ i *
{:else if part.type === 'event' && part.event} {:else if part.type === 'event' && part.event}
<div class="nostr-link-event"> <div class="nostr-link-event">
<div class="nostr-link-event-header"> <div class="nostr-link-event-header">
<UserBadge pubkey={part.event.pubkey} disableLink={true} /> <span class="nostr-link-event-author">{getAuthorName(part.event.pubkey)}</span>
<span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span> <span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span>
</div> </div>
<div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div> <div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div>
@ -2039,13 +2118,22 @@ i *
<div class="zap-amount">{zapData.amount}</div> <div class="zap-amount">{zapData.amount}</div>
<div class="zap-details"> <div class="zap-details">
{#if zapData.senderPubkey} {#if zapData.senderPubkey}
<span>From <UserBadge pubkey={zapData.senderPubkey} disableLink={true} /></span> <span class="zap-detail-item">
<span class="zap-detail-label">From</span>
<span class="zap-detail-value">{getAuthorName(zapData.senderPubkey)}</span>
</span>
{/if} {/if}
{#if zapData.recipientPubkey && zapData.recipientPubkey !== profileOwnerPubkeyHex} {#if zapData.recipientPubkey && zapData.recipientPubkey !== profileOwnerPubkeyHex}
<span>To <UserBadge pubkey={zapData.recipientPubkey} disableLink={true} /></span> <span class="zap-detail-item">
<span class="zap-detail-label">To</span>
<span class="zap-detail-value">{getAuthorName(zapData.recipientPubkey)}</span>
</span>
{/if} {/if}
{#if zapData.eventId} {#if zapData.eventId}
<span>on event {zapData.eventId.slice(0, 8)}...</span> <span class="zap-detail-item">
<span class="zap-detail-label">on event</span>
<span class="zap-detail-value">{zapData.eventId.slice(0, 8)}...</span>
</span>
{/if} {/if}
{#if zapData.comment} {#if zapData.comment}
<div class="zap-comment">{zapData.comment}</div> <div class="zap-comment">{zapData.comment}</div>
@ -2078,7 +2166,7 @@ i *
{:else if part.type === 'event' && part.event} {:else if part.type === 'event' && part.event}
<div class="nostr-link-event"> <div class="nostr-link-event">
<div class="nostr-link-event-header"> <div class="nostr-link-event-header">
<UserBadge pubkey={part.event.pubkey} disableLink={true} /> <span class="nostr-link-event-author">{getAuthorName(part.event.pubkey)}</span>
<span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span> <span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span>
</div> </div>
<div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div> <div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div>
@ -2153,7 +2241,7 @@ i *
{:else if part.type === 'event' && part.event} {:else if part.type === 'event' && part.event}
<div class="nostr-link-event"> <div class="nostr-link-event">
<div class="nostr-link-event-header"> <div class="nostr-link-event-header">
<UserBadge pubkey={part.event.pubkey} disableLink={true} /> <span class="nostr-link-event-author">{getAuthorName(part.event.pubkey)}</span>
<span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span> <span class="nostr-link-event-time">{formatMessageTime(part.event.created_at)}</span>
</div> </div>
<div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div> <div class="nostr-link-event-content">{part.event.content || getEventContext(part.event)}</div>
@ -3038,17 +3126,33 @@ i *
.message-icon { .message-icon {
width: 1.25rem; width: 1.25rem;
height: 1.25rem; height: 1.25rem;
filter: var(--icon-filter, none); filter: var(--icon-filter, brightness(0) saturate(100%) invert(1));
opacity: 0.8;
}
:global([data-theme="light"]) .message-icon {
filter: brightness(0) saturate(100%);
opacity: 0.7;
}
:global([data-theme="dark"]) .message-icon,
:global([data-theme="black"]) .message-icon {
filter: brightness(0) saturate(100%) invert(1);
opacity: 0.8;
}
.message-action-button:hover .message-icon {
opacity: 1;
} }
.quoted-event { .quoted-event {
margin-bottom: 1rem; margin-bottom: 0.75rem;
padding: 0.75rem; padding: 0.5rem;
background: var(--bg-secondary); background: var(--bg-secondary, var(--bg-primary));
border: 1px solid var(--border-color); color: var(--text-muted, var(--text-secondary));
border-left: 3px solid var(--accent, #007bff); border-radius: 4px;
border-radius: 0.375rem; border-left: 2px solid var(--border-light, var(--border-color));
font-size: 0.875rem; opacity: 0.8;
} }
:global([data-theme="light"]) .quoted-event { :global([data-theme="light"]) .quoted-event {
@ -3069,50 +3173,35 @@ i *
.quoted-event-header { .quoted-event-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.375rem;
margin-bottom: 0.5rem; margin-bottom: 0.25rem;
flex-wrap: wrap; font-size: 0.75rem;
color: var(--text-muted, var(--text-secondary));
}
.quoted-event-label {
font-weight: 500;
color: var(--text-muted, var(--text-secondary));
} }
.quoted-event-time { .quoted-event-time {
font-size: 0.75rem; color: var(--text-muted, var(--text-secondary));
color: var(--text-muted); font-size: 0.7rem;
margin-left: auto; margin-left: auto;
} }
.quoted-event-content { .quoted-event-content {
color: var(--text-secondary); font-size: 0.8rem;
color: var(--text-muted, var(--text-secondary));
line-height: 1.4;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
line-height: 1.5;
max-height: 8rem; max-height: 8rem;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
} }
.quoted-event-content::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2rem;
background: linear-gradient(to bottom, transparent, var(--bg-secondary));
pointer-events: none;
}
:global([data-theme="light"]) .quoted-event-content::after {
background: linear-gradient(to bottom, transparent, #f5f5f5);
}
:global([data-theme="dark"]) .quoted-event-content::after {
background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.05));
}
:global([data-theme="black"]) .quoted-event-content::after {
background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.03));
}
.quoted-event-loading { .quoted-event-loading {
opacity: 0.6; opacity: 0.6;
@ -3331,27 +3420,39 @@ i *
.zap-details { .zap-details {
display: flex; display: flex;
flex-direction: column; flex-wrap: wrap;
gap: 0.25rem; gap: 0.5rem;
font-size: 0.875rem; font-size: 0.7rem;
color: var(--text-primary); color: var(--text-muted, var(--text-secondary));
line-height: 1.5; line-height: 1.4;
margin-top: 0.25rem;
} }
.zap-details span { .zap-detail-item {
display: flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.25rem;
}
.zap-detail-label {
color: var(--text-muted, var(--text-secondary));
font-weight: 500;
}
.zap-detail-value {
color: var(--text-muted, var(--text-secondary));
} }
.zap-comment { .zap-comment {
margin-top: 0.5rem; margin-top: 0.375rem;
padding: 0.75rem; padding: 0.375rem 0.5rem;
background: var(--bg-secondary); background: var(--bg-secondary, var(--bg-primary));
border-radius: 0.5rem; border-radius: 4px;
border-left: 2px solid var(--accent); border-left: 2px solid var(--border-light, var(--border-color));
font-style: italic; font-style: italic;
color: var(--text-secondary); font-size: 0.75rem;
color: var(--text-muted, var(--text-secondary));
opacity: 0.8;
} }
.zap-receipt { .zap-receipt {
@ -3509,11 +3610,20 @@ i *
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
line-height: 1;
font-size: 0.75rem;
color: var(--text-muted, var(--text-secondary));
}
.nostr-link-event-author {
font-weight: 500;
color: var(--text-muted, var(--text-secondary));
} }
.nostr-link-event-time { .nostr-link-event-time {
font-size: 0.75rem; font-size: 0.7rem;
color: var(--text-muted); color: var(--text-muted, var(--text-secondary));
margin-left: auto;
} }
.nostr-link-event-content { .nostr-link-event-content {

Loading…
Cancel
Save