Browse Source

bug-fixes

master
Silberengel 1 month ago
parent
commit
dffd414a5e
  1. 113
      src/lib/components/layout/ProfileBadge.svelte
  2. 167
      src/lib/modules/feed/FeedPost.svelte
  3. 135
      src/lib/modules/feed/HighlightCard.svelte
  4. 154
      src/lib/modules/profiles/ProfilePage.svelte
  5. 45
      src/routes/topics/[name]/+page.svelte

113
src/lib/components/layout/ProfileBadge.svelte

@ -191,13 +191,13 @@
{/if} {/if}
{/if} {/if}
{#if !pictureOnly} {#if !pictureOnly}
<div class="flex flex-col min-w-0 flex-1 max-w-full"> <div class="flex flex-col min-w-0 flex-1 max-w-full profile-badge-content">
<div class="flex items-center gap-2 flex-wrap min-w-0 max-w-full"> <div class="flex items-center gap-2 flex-wrap min-w-0 max-w-full nip05-container">
<span class="truncate min-w-0 max-w-full"> <span class="truncate min-w-0 max-w-full">
{profile?.name || shortenedNpub} {profile?.name || shortenedNpub}
</span> </span>
{#if profile?.nip05 && profile.nip05.length > 0} {#if profile?.nip05 && profile.nip05.length > 0}
<span class="nip05-text text-fog-text-light dark:text-fog-dark-text-light min-w-0 max-w-full"> <span class="nip05-text text-fog-text-light dark:text-fog-dark-text-light min-w-0 break-nip05">
{profile.nip05[0]} {profile.nip05[0]}
</span> </span>
{/if} {/if}
@ -210,10 +210,30 @@
.profile-badge { .profile-badge {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
max-width: 100%; max-width: 100% !important;
width: 100%; width: auto !important;
min-width: 0 !important;
transition: opacity 0.2s; transition: opacity 0.2s;
overflow: hidden; overflow: visible !important;
word-break: break-word !important;
overflow-wrap: anywhere !important;
box-sizing: border-box !important;
display: inline-flex !important;
flex-shrink: 1 !important;
}
.profile-badge-content {
min-width: 0 !important;
max-width: 100% !important;
width: 100% !important;
box-sizing: border-box !important;
}
.nip05-container {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box !important;
min-width: 0 !important;
} }
.profile-badge:hover { .profile-badge:hover {
@ -263,23 +283,86 @@
min-height: 1.5rem; min-height: 1.5rem;
} }
/* Apply break-all only on wider screens (not in narrow screen media query) */
@media (min-width: 641px) {
.nip05-text {
font-size: 0.875em;
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
max-width: 100% !important;
white-space: normal !important;
flex-shrink: 1 !important;
min-width: 0 !important;
display: inline-block !important;
width: auto !important;
max-width: 100% !important;
box-sizing: border-box !important;
hyphens: auto;
overflow: visible !important;
}
.break-nip05 {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
display: inline-block !important;
}
}
/* Base styles for NIP-05 text that apply on all screen sizes */
.nip05-text { .nip05-text {
font-size: 0.875em; font-size: 0.875em;
word-break: break-word; max-width: 100% !important;
overflow-wrap: break-word; flex-shrink: 1 !important;
max-width: 100%; min-width: 0 !important;
overflow: hidden; display: inline-block !important;
text-overflow: ellipsis; width: auto !important;
box-sizing: border-box !important;
hyphens: auto;
}
.break-nip05 {
max-width: 100% !important;
display: inline-block !important;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.profile-badge { .profile-badge {
max-width: 100%; max-width: 100% !important;
width: 100% !important;
} }
.nip05-text { .nip05-container {
max-width: 100%; flex-direction: column !important;
word-break: break-all; align-items: flex-start !important;
width: 100% !important;
}
/* Truncate handle/name to 10 characters on narrow screens */
.profile-badge-content .truncate {
max-width: 10ch !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
display: inline-block !important;
}
/* Truncate NIP-05 to 10 characters on narrow screens */
/* Override both the class selectors and Tailwind's break-all utility */
.nip05-text,
.break-nip05 {
max-width: 10ch !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
display: inline-block !important;
word-break: normal !important;
overflow-wrap: normal !important;
word-wrap: normal !important;
box-sizing: border-box !important;
} }
} }
</style> </style>

167
src/lib/modules/feed/FeedPost.svelte

@ -840,12 +840,12 @@
<div class="post-header flex items-center justify-between gap-2 mb-2"> <div class="post-header flex items-center justify-between gap-2 mb-2">
<div class="flex items-center gap-2 flex-1 min-w-0 post-header-left"> <div class="flex items-center gap-2 flex-1 min-w-0 post-header-left">
<div class="flex-shrink-0"> <div class="flex-shrink-1 min-w-0 max-w-full profile-badge-wrapper">
<ProfileBadge pubkey={post.pubkey} /> <ProfileBadge pubkey={post.pubkey} />
</div> </div>
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0 whitespace-nowrap" style="font-size: 0.75em;">{getRelativeTime()}</span> <span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0" style="font-size: 0.75em;">{getRelativeTime()}</span>
{#if getClientName()} {#if getClientName()}
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0 whitespace-nowrap" style="font-size: 0.75em;">via {getClientName()}</span> <span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0" style="font-size: 0.75em;">via {getClientName()}</span>
{/if} {/if}
{#if post.kind === KIND.DISCUSSION_THREAD} {#if post.kind === KIND.DISCUSSION_THREAD}
{@const topics = getTopics()} {@const topics = getTopics()}
@ -903,9 +903,9 @@
<div class="flex-shrink-0"> <div class="flex-shrink-0">
<ProfileBadge pubkey={post.pubkey} inline={true} /> <ProfileBadge pubkey={post.pubkey} inline={true} />
</div> </div>
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0 whitespace-nowrap" style="font-size: 0.75em;">{getRelativeTime()}</span> <span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0" style="font-size: 0.75em;">{getRelativeTime()}</span>
{#if getClientName()} {#if getClientName()}
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0 whitespace-nowrap" style="font-size: 0.75em;">via {getClientName()}</span> <span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0" style="font-size: 0.75em;">via {getClientName()}</span>
{/if} {/if}
{#if post.kind === KIND.DISCUSSION_THREAD} {#if post.kind === KIND.DISCUSSION_THREAD}
{@const topics = getTopics()} {@const topics = getTopics()}
@ -1110,11 +1110,18 @@
border-radius: 0.25rem; border-radius: 0.25rem;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.Feed-post { .Feed-post {
padding: 0.75rem; padding: 0.75rem;
width: 100%;
max-width: 100%;
} }
} }
@ -1172,17 +1179,42 @@
.post-content { .post-content {
line-height: 1.6; line-height: 1.6;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: anywhere;
word-break: break-word; word-break: break-word;
padding-top: 1rem; padding-top: 1rem;
width: 100%;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
}
.post-content :global(.nostr-event-link),
.post-content :global(a[href^="nostr:"]) {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
display: inline-block !important;
box-sizing: border-box !important;
}
.post-content :global(.profile-badge) {
max-width: 100% !important;
min-width: 0 !important;
flex-shrink: 1 !important;
word-break: break-word !important;
overflow-wrap: anywhere !important;
flex-wrap: wrap !important;
} }
.word-wrap { .word-wrap {
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: anywhere;
word-break: break-word; word-break: break-word;
} }
.media-urls { .media-urls {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -1192,12 +1224,20 @@
.media-url-link { .media-url-link {
word-break: break-all; word-break: break-all;
overflow-wrap: anywhere;
text-align: left; /* Ensure left alignment */ text-align: left; /* Ensure left alignment */
max-width: 100%;
} }
.nostr-event-link { .nostr-event-link {
word-break: break-all; word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
text-decoration: underline; text-decoration: underline;
max-width: 100% !important;
display: inline-block !important;
box-sizing: border-box !important;
} }
.nostr-event-link:hover { .nostr-event-link:hover {
@ -1289,11 +1329,62 @@
gap: 0.5rem; gap: 0.5rem;
min-width: 0; min-width: 0;
flex-wrap: wrap; flex-wrap: wrap;
width: 100%;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
word-break: break-word;
overflow-wrap: anywhere;
} }
.post-header-left { .post-header-left {
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
}
.post-header-left > span {
word-break: break-word !important;
overflow-wrap: anywhere !important;
white-space: normal !important;
max-width: 100%;
}
.profile-badge-wrapper {
min-width: 0 !important;
max-width: 100% !important;
flex-shrink: 1 !important;
overflow: visible !important;
}
.post-header-left :global(.profile-badge) {
max-width: 100% !important;
width: auto !important;
min-width: 0 !important;
word-break: break-word !important;
overflow-wrap: anywhere !important;
box-sizing: border-box !important;
flex-shrink: 1 !important;
flex-wrap: wrap !important;
}
/* Apply break-all only on wider screens (not in narrow screen media query) */
@media (min-width: 641px) {
.post-header-left :global(.nip05-text),
.post-header-left :global(.break-nip05) {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
width: 100% !important;
display: block !important;
box-sizing: border-box !important;
}
} }
@media (max-width: 640px) { @media (max-width: 640px) {
@ -1309,11 +1400,68 @@
gap: 0.5rem; gap: 0.5rem;
} }
.post-header-left > span {
white-space: normal !important;
word-break: break-word !important;
overflow-wrap: anywhere !important;
max-width: 100%;
flex-shrink: 1;
min-width: 0;
}
.post-header-left {
width: 100%;
flex-wrap: wrap;
gap: 0.5rem;
}
.post-header-actions { .post-header-actions {
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
} }
.profile-badge-wrapper {
max-width: 100% !important;
width: 100% !important;
flex-shrink: 1 !important;
min-width: 0 !important;
}
.post-header-left :global(.profile-badge) {
max-width: 100% !important;
width: 100% !important;
min-width: 0 !important;
flex-shrink: 1 !important;
}
.post-header-left :global(.nip05-container) {
flex-direction: column !important;
align-items: flex-start !important;
width: 100% !important;
}
/* Truncate NIP-05 to 10 characters on narrow screens */
/* Override both the class selectors and Tailwind's break-all utility */
.post-header-left :global(.nip05-text),
.post-header-left :global(.break-nip05),
.post-header-left :global(.nip05-text.break-all),
.post-header-left :global(.break-nip05.break-all),
.post-header-left :global(span.nip05-text),
.post-header-left :global(span.break-nip05),
.post-header-left :global(span.nip05-text.break-all),
.post-header-left :global(span.break-nip05.break-all) {
max-width: 10ch !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
word-break: normal !important;
overflow-wrap: normal !important;
word-wrap: normal !important;
display: inline-block !important;
width: auto !important;
box-sizing: border-box !important;
}
} }
.post-header-divider { .post-header-divider {
@ -1352,7 +1500,8 @@
.post-title { .post-title {
word-break: break-word; word-break: break-word;
overflow-wrap: break-word; overflow-wrap: anywhere;
max-width: 100%;
} }
} }

135
src/lib/modules/feed/HighlightCard.svelte

@ -395,6 +395,12 @@
border: 1px solid var(--fog-border, #e5e7eb); border: 1px solid var(--fog-border, #e5e7eb);
border-radius: 0.25rem; border-radius: 0.25rem;
position: relative; position: relative;
overflow: hidden;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
} }
:global(.dark) .highlight-card { :global(.dark) .highlight-card {
@ -411,6 +417,31 @@
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
flex-wrap: wrap; flex-wrap: wrap;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
}
.highlight-meta :global(.profile-badge) {
max-width: 100% !important;
min-width: 0 !important;
flex-shrink: 1 !important;
word-break: break-word !important;
overflow-wrap: anywhere !important;
}
/* Apply break-all only on wider screens (not in narrow screen media query) */
@media (min-width: 641px) {
.highlight-meta :global(.nip05-text),
.highlight-meta :global(.break-nip05) {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
}
} }
.highlight-content { .highlight-content {
@ -419,6 +450,12 @@
background: var(--fog-highlight, #f3f4f6); background: var(--fog-highlight, #f3f4f6);
border-radius: 0.25rem; border-radius: 0.25rem;
border-left: 3px solid #fbbf24; border-left: 3px solid #fbbf24;
width: 100%;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
word-break: break-word;
overflow-wrap: anywhere;
} }
:global(.dark) .highlight-content { :global(.dark) .highlight-content {
@ -430,6 +467,12 @@
padding-top: 0.5rem; padding-top: 0.5rem;
border-top: 1px solid var(--fog-border, #e5e7eb); border-top: 1px solid var(--fog-border, #e5e7eb);
margin-top: 0.5rem; margin-top: 0.5rem;
width: 100%;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
word-break: break-word;
overflow-wrap: anywhere;
} }
:global(.dark) .source-event-link { :global(.dark) .source-event-link {
@ -451,8 +494,29 @@
color: var(--fog-accent, #64748b); color: var(--fog-accent, #64748b);
text-decoration: none; text-decoration: none;
font-size: 0.875rem; font-size: 0.875rem;
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100%;
display: inline-block;
box-sizing: border-box;
}
/* Ensure nostr: addresses break properly */
.highlight-content :global(.nostr-event-link),
.highlight-content :global(a[href^="nostr:"]),
.source-event-link :global(.nostr-event-link),
.source-event-link :global(a[href^="nostr:"]) {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
display: inline-block !important;
} }
.source-link:hover { .source-link:hover {
text-decoration: underline; text-decoration: underline;
} }
@ -520,4 +584,75 @@
.bookmark-indicator.bookmarked { .bookmark-indicator.bookmarked {
filter: grayscale(0%); filter: grayscale(0%);
} }
@media (max-width: 640px) {
.highlight-card {
padding: 0.75rem;
width: 100%;
max-width: 100%;
}
.highlight-content {
padding: 0.5rem;
width: 100%;
max-width: 100%;
}
.source-event-link {
width: 100%;
max-width: 100%;
}
.source-link {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
display: block !important;
width: 100% !important;
}
.source-event-link a,
.source-event-link span {
word-break: break-all !important;
overflow-wrap: anywhere !important;
word-wrap: break-word !important;
white-space: normal !important;
max-width: 100% !important;
}
.highlight-meta {
flex-wrap: wrap;
width: 100%;
max-width: 100%;
}
.highlight-meta > span {
word-break: break-word;
overflow-wrap: anywhere;
white-space: normal;
}
/* Truncate NIP-05 to 10 characters on narrow screens */
/* Override both the class selectors and Tailwind's break-all utility */
.highlight-meta :global(.nip05-text),
.highlight-meta :global(.break-nip05),
.highlight-meta :global(.nip05-text.break-all),
.highlight-meta :global(.break-nip05.break-all),
.highlight-meta :global(span.nip05-text),
.highlight-meta :global(span.break-nip05),
.highlight-meta :global(span.nip05-text.break-all),
.highlight-meta :global(span.break-nip05.break-all) {
max-width: 10ch !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
word-break: normal !important;
overflow-wrap: normal !important;
word-wrap: normal !important;
display: inline-block !important;
box-sizing: border-box !important;
}
}
</style> </style>

154
src/lib/modules/profiles/ProfilePage.svelte

@ -28,7 +28,7 @@
let wallComments = $state<NostrEvent[]>([]); // Kind 1111 comments on the wall let wallComments = $state<NostrEvent[]>([]); // Kind 1111 comments on the wall
let loading = $state(true); let loading = $state(true);
let loadingWall = $state(false); let loadingWall = $state(false);
let activeTab = $state<'pins' | 'notifications' | 'interactions' | 'wall' | 'bookmarks'>('pins'); let activeTab = $state<'pins' | 'notifications' | 'interactions' | 'wall'>('pins');
let nip05Validations = $state<Record<string, boolean | null>>({}); // null = checking, true = valid, false = invalid let nip05Validations = $state<Record<string, boolean | null>>({}); // null = checking, true = valid, false = invalid
// Compute pubkey from route params // Compute pubkey from route params
let profilePubkey = $derived.by(() => decodePubkey($page.params.pubkey)); let profilePubkey = $derived.by(() => decodePubkey($page.params.pubkey));
@ -43,10 +43,6 @@
// Pins state // Pins state
let pins = $state<NostrEvent[]>([]); let pins = $state<NostrEvent[]>([]);
// Bookmarks state
let bookmarks = $state<NostrEvent[]>([]);
let loadingBookmarks = $state(false);
// Cleanup tracking // Cleanup tracking
let isMounted = $state(true); let isMounted = $state(true);
let activeFetchPromises = $state<Set<Promise<any>>>(new Set()); let activeFetchPromises = $state<Set<Promise<any>>>(new Set());
@ -272,133 +268,6 @@
} }
} }
async function loadBookmarks(pubkey: string) {
if (!isMounted) return;
loadingBookmarks = true;
try {
// Fetch the user's bookmark list (kind 10003)
const profileRelays = relayManager.getProfileReadRelays();
const bookmarkLists = await nostrClient.fetchEvents(
[{ kinds: [KIND.BOOKMARKS], authors: [pubkey], limit: 400 }],
profileRelays,
{ useCache: 'cache-first', cacheResults: true, timeout: config.mediumTimeout }
);
if (!isMounted || bookmarkLists.length === 0) {
if (isMounted) {
bookmarks = [];
loadingBookmarks = false;
}
return;
}
// Extract event IDs from bookmark lists
const bookmarkedIds = new Set<string>();
for (const bookmarkList of bookmarkLists) {
for (const tag of bookmarkList.tags) {
if (tag[0] === 'e' && tag[1]) {
bookmarkedIds.add(tag[1]);
}
}
}
if (bookmarkedIds.size === 0) {
if (isMounted) {
bookmarks = [];
loadingBookmarks = false;
}
return;
}
// Load from cache first (fast - instant display)
try {
const { getEvent } = await import('../../services/cache/event-cache.js');
const cachedBookmarks: NostrEvent[] = [];
for (const id of bookmarkedIds) {
const cached = await getEvent(id);
if (cached) {
cachedBookmarks.push(cached);
}
}
if (cachedBookmarks.length > 0 && isMounted) {
bookmarks = cachedBookmarks.sort((a, b) => b.created_at - a.created_at);
loadingBookmarks = false; // Show cached content immediately
}
} catch (error) {
// Cache error is non-critical
}
// Stream fresh data from relays (progressive enhancement)
// Optimized: Fetch all bookmarked events in parallel batches
const batchSize = 100;
const bookmarkedIdsArray = Array.from(bookmarkedIds);
const batches: string[][] = [];
for (let i = 0; i < bookmarkedIdsArray.length; i += batchSize) {
batches.push(bookmarkedIdsArray.slice(i, i + batchSize));
}
// Fetch all batches in parallel
const batchPromises = batches.map(batch =>
nostrClient.fetchEvents(
[{ ids: batch, limit: batch.length }],
profileRelays,
{
useCache: 'cache-first', // Already shown cache above, now stream updates
cacheResults: true,
timeout: config.mediumTimeout,
onUpdate: (newBookmarks) => {
if (!isMounted) return;
// Merge with existing bookmarks
const bookmarkMap = new Map(bookmarks.map(b => [b.id, b]));
for (const bookmark of newBookmarks) {
bookmarkMap.set(bookmark.id, bookmark);
}
bookmarks = Array.from(bookmarkMap.values()).sort((a, b) => b.created_at - a.created_at);
loadingBookmarks = false;
}
}
)
);
// Track all batch promises for cleanup
for (const promise of batchPromises) {
activeFetchPromises.add(promise);
}
try {
// Wait for all batches to complete
const allBatchResults = await Promise.all(batchPromises);
if (!isMounted) return;
// Merge final results with existing bookmarks (onUpdate may have already updated some)
// This ensures we don't lose any intermediate updates from streaming
const bookmarkMap = new Map(bookmarks.map(b => [b.id, b]));
const allBookmarkedEvents = allBatchResults.flat();
for (const bookmark of allBookmarkedEvents) {
bookmarkMap.set(bookmark.id, bookmark);
}
bookmarks = Array.from(bookmarkMap.values()).sort((a, b) => b.created_at - a.created_at);
} finally {
// Clean up all batch promises
for (const promise of batchPromises) {
activeFetchPromises.delete(promise);
}
}
} catch (error) {
// Failed to load bookmarks
if (isMounted) {
bookmarks = [];
}
} finally {
if (isMounted) {
loadingBookmarks = false;
}
}
}
async function loadNotifications(pubkey: string) { async function loadNotifications(pubkey: string) {
if (!isMounted) return; if (!isMounted) return;
try { try {
@ -758,9 +627,6 @@
// Step 2: Load pins for the profile being viewed // Step 2: Load pins for the profile being viewed
await loadPins(pubkey); await loadPins(pubkey);
// Step 2.5: Load bookmarks for the profile being viewed
await loadBookmarks(pubkey);
// Step 3: Load notifications or interactions // Step 3: Load notifications or interactions
if (isOwnProfile) { if (isOwnProfile) {
await loadNotifications(pubkey); await loadNotifications(pubkey);
@ -917,12 +783,6 @@
Interactions ({interactionsWithMe.length}) Interactions ({interactionsWithMe.length})
</button> </button>
{/if} {/if}
<button
onclick={() => activeTab = 'bookmarks'}
class="px-2 sm:px-4 py-2 font-semibold whitespace-nowrap flex-shrink-0 {activeTab === 'bookmarks' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}"
>
Bookmarks ({bookmarks.length})
</button>
</div> </div>
{#if activeTab === 'pins'} {#if activeTab === 'pins'}
@ -997,18 +857,6 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{:else if activeTab === 'bookmarks'}
{#if loadingBookmarks}
<p class="text-fog-text-light dark:text-fog-dark-text-light">Loading bookmarks...</p>
{:else if bookmarks.length === 0}
<p class="text-fog-text-light dark:text-fog-dark-text-light">No bookmarks yet.</p>
{:else}
<div class="bookmarks-list">
{#each bookmarks as bookmark (bookmark.id)}
<FeedPost post={bookmark} />
{/each}
</div>
{/if}
{/if} {/if}
</div> </div>
{:else} {:else}

45
src/routes/topics/[name]/+page.svelte

@ -155,7 +155,7 @@
<Header /> <Header />
<main class="container mx-auto px-4 py-8"> <main class="topic-main container mx-auto px-2 sm:px-4 py-8">
<div class="topic-content"> <div class="topic-content">
<div class="topic-header mb-6"> <div class="topic-header mb-6">
<h1 class="font-bold text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;"> <h1 class="font-bold text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;">
@ -227,15 +227,27 @@
<style> <style>
.topic-main {
width: 100%;
max-width: 100%;
overflow: hidden;
box-sizing: border-box;
}
.topic-content { .topic-content {
max-width: var(--content-width); max-width: var(--content-width);
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.topic-content { .topic-content {
max-width: 100%; max-width: 100%;
width: 100%;
} }
} }
@ -244,12 +256,20 @@
border-bottom: 1px solid var(--fog-border, #e5e7eb); border-bottom: 1px solid var(--fog-border, #e5e7eb);
padding-bottom: 1rem; padding-bottom: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
overflow: hidden;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.topic-header { .topic-header {
padding: 0 0.5rem; padding: 0 0.5rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
width: 100%;
max-width: 100%;
} }
} }
@ -268,6 +288,8 @@
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
overflow: hidden; overflow: hidden;
width: 100%;
max-width: 100%;
} }
.event-item { .event-item {
@ -276,12 +298,31 @@
border-radius: 0.5rem; border-radius: 0.5rem;
background: var(--fog-post, #ffffff); background: var(--fog-post, #ffffff);
overflow: hidden; overflow: hidden;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-break: break-word;
overflow-wrap: anywhere;
}
.event-item :global(.post-header-left) > :global(span) {
white-space: normal !important;
word-break: break-word;
overflow-wrap: anywhere;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.event-item { .event-item {
padding: 0.5rem; padding: 0.5rem;
margin: 0 0.5rem; margin: 0;
width: 100%;
max-width: 100%;
}
.event-item :global(.post-header-left) > :global(span) {
white-space: normal !important;
word-break: break-word;
overflow-wrap: anywhere;
} }
} }

Loading…
Cancel
Save