Browse Source

bug-fixes

master
Silberengel 1 month ago
parent
commit
17954ef4b7
  1. 47
      .dockerignore
  2. 22
      Dockerfile
  3. 11
      docker-compose.yml
  4. 15
      docker-entrypoint.sh
  5. 1
      httpd.conf.template
  6. 6
      src/app.css
  7. 21
      src/lib/components/layout/ProfileBadge.svelte
  8. 25
      src/lib/modules/comments/Comment.svelte
  9. 7
      src/lib/modules/comments/CommentThread.svelte
  10. 33
      src/lib/modules/discussions/DiscussionCard.svelte
  11. 7
      src/lib/modules/discussions/DiscussionList.svelte
  12. 7
      src/lib/modules/discussions/DiscussionView.svelte
  13. 131
      src/lib/modules/feed/FeedPost.svelte
  14. 16
      src/lib/modules/feed/HighlightCard.svelte
  15. 1
      src/lib/services/nostr/config.ts
  16. 6
      src/routes/discussions/+page.svelte
  17. 29
      src/routes/feed/+page.svelte
  18. 24
      src/routes/topics/+page.svelte
  19. 24
      src/routes/topics/[name]/+page.svelte

47
.dockerignore

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
# Dependencies
node_modules
npm-debug.log
# Note: package-lock.json is needed for npm ci, so don't exclude it
# Build outputs
build
.svelte-kit
package
# Development files
.env
.env.*
!.env.example
# Git
.git
.gitignore
.gitattributes
# IDE
.vscode
.idea
*.swp
*.swo
*~
# Documentation
README.md
README_SETUP.md
*.md
# CI/CD
.github
.gitlab-ci.yml
# Test files
**/*.test.ts
**/*.test.js
**/*.spec.ts
**/*.spec.js
coverage
# Misc
.DS_Store
*.log
*.tmp

22
Dockerfile

@ -4,14 +4,12 @@ WORKDIR /app @@ -4,14 +4,12 @@ WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Optional build args - will use defaults from config.ts if not provided
# If ARG is not provided, ENV will be empty and config.ts will use defaults
ARG VITE_DEFAULT_RELAYS
ARG VITE_ZAP_THRESHOLD
ARG VITE_THREAD_TIMEOUT_DAYS
ARG VITE_PWA_ENABLED
ENV VITE_DEFAULT_RELAYS=${VITE_DEFAULT_RELAYS}
ENV VITE_ZAP_THRESHOLD=${VITE_ZAP_THRESHOLD}
ENV VITE_THREAD_TIMEOUT_DAYS=${VITE_THREAD_TIMEOUT_DAYS}
ENV VITE_PWA_ENABLED=${VITE_PWA_ENABLED}
RUN npm run build
FROM httpd:alpine
@ -19,8 +17,20 @@ RUN apk add --no-cache gettext && \ @@ -19,8 +17,20 @@ RUN apk add --no-cache gettext && \
mkdir -p /usr/local/apache2/logs && \
chown -R daemon:daemon /usr/local/apache2/logs
COPY --from=builder /app/build /usr/local/apache2/htdocs/
# Ensure healthz.json exists (copy from public if not in build, or create if missing)
COPY --from=builder /app/public/healthz.json /usr/local/apache2/htdocs/healthz.json
# Ensure healthz.json exists (SvelteKit copies public/healthz.json to build/)
# If it doesn't exist for some reason, create a default one
RUN if [ ! -f /usr/local/apache2/htdocs/healthz.json ]; then \
echo '{"status":"ok","service":"aitherboard","version":"unknown","buildTime":"'$(date -Iseconds)'","timestamp":'$(date +%s)'}' > /usr/local/apache2/htdocs/healthz.json && \
echo "Created default healthz.json"; \
else \
echo "healthz.json found in build output"; \
fi
# Verify 200.html exists (required for SPA routing)
RUN if [ ! -f /usr/local/apache2/htdocs/200.html ]; then \
echo "ERROR: 200.html not found! SPA routing will not work." && exit 1; \
else \
echo "200.html found - SPA routing configured correctly"; \
fi
COPY httpd.conf.template /usr/local/apache2/conf/httpd.conf.template
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

11
docker-compose.yml

@ -1,12 +1,13 @@ @@ -1,12 +1,13 @@
services:
aitherboard:
container_name: aitherboard
build:
context: .
args:
VITE_DEFAULT_RELAYS: "wss://theforest.nostr1.com,wss://nostr21.com,wss://nostr.land,wss://orly-relay.imwald.eu"
VITE_ZAP_THRESHOLD: "1"
VITE_THREAD_TIMEOUT_DAYS: "30"
VITE_PWA_ENABLED: "true"
# Optional: override defaults from config.ts if needed
# Uncomment and modify if you want custom values:
# args:
# VITE_DEFAULT_RELAYS: "wss://theforest.nostr1.com,wss://nostr21.com,wss://nostr.land"
# VITE_THREAD_TIMEOUT_DAYS: "30"
ports:
- "9876:9876"
environment:

15
docker-entrypoint.sh

@ -22,10 +22,21 @@ ls -la /usr/local/apache2/htdocs/ | head -20 @@ -22,10 +22,21 @@ ls -la /usr/local/apache2/htdocs/ | head -20
echo "File count: $(find /usr/local/apache2/htdocs -type f | wc -l)"
echo "Checking if port $PORT is available..."
if ! netstat -tuln 2>/dev/null | grep -q ":$PORT "; then
# Use ss (socket statistics) which is available in Alpine, fallback to netstat if available
if command -v ss >/dev/null 2>&1; then
if ! ss -tuln 2>/dev/null | grep -q ":$PORT "; then
echo "Port $PORT appears to be available"
else
else
echo "WARNING: Port $PORT might be in use"
fi
elif command -v netstat >/dev/null 2>&1; then
if ! netstat -tuln 2>/dev/null | grep -q ":$PORT "; then
echo "Port $PORT appears to be available"
else
echo "WARNING: Port $PORT might be in use"
fi
else
echo "Port check skipped (ss/netstat not available)"
fi
echo "Starting Apache on port $PORT..."

1
httpd.conf.template

@ -6,6 +6,7 @@ LoadModule log_config_module modules/mod_log_config.so @@ -6,6 +6,7 @@ LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule dir_module modules/mod_dir.so
LoadModule deflate_module modules/mod_deflate.so
LoadModule setenvif_module modules/mod_setenvif.so
PidFile "/usr/local/apache2/logs/httpd.pid"
ErrorLog "/proc/self/fd/2"

6
src/app.css

@ -13,15 +13,15 @@ @@ -13,15 +13,15 @@
/* Base text size preferences - will be overridden by media queries if not specified */
[data-text-size='small'] {
--text-size: 10px;
--text-size: 6px;
}
[data-text-size='medium'] {
--text-size: 12px;
--text-size: 8px;
}
[data-text-size='large'] {
--text-size: 14px;
--text-size: 10px;
}
[data-line-spacing='tight'] {

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

@ -172,8 +172,8 @@ @@ -172,8 +172,8 @@
}}
onload={(e) => {
// If compressed URL fails, try original as fallback
if (imageError && compressedPictureUrl !== profile.picture) {
const img = e.currentTarget;
if (imageError && profile?.picture && compressedPictureUrl !== profile.picture) {
const img = e.currentTarget as HTMLImageElement;
img.src = profile.picture;
imageError = false;
}
@ -192,12 +192,12 @@ @@ -192,12 +192,12 @@
{/if}
{#if !pictureOnly}
<div class="flex flex-col min-w-0 flex-1 max-w-full">
<div class="flex items-center gap-2 flex-wrap min-w-0">
<div class="flex items-center gap-2 flex-wrap min-w-0 max-w-full">
<span class="truncate min-w-0 max-w-full">
{profile?.name || shortenedNpub}
</span>
{#if profile?.nip05 && profile.nip05.length > 0}
<span class="nip05-text text-fog-text-light dark:text-fog-dark-text-light min-w-0 break-all">
<span class="nip05-text text-fog-text-light dark:text-fog-dark-text-light min-w-0 max-w-full">
{profile.nip05[0]}
</span>
{/if}
@ -268,5 +268,18 @@ @@ -268,5 +268,18 @@
word-break: break-word;
overflow-wrap: break-word;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640px) {
.profile-badge {
max-width: 100%;
}
.nip05-text {
max-width: 100%;
word-break: break-all;
}
}
</style>

25
src/lib/modules/comments/Comment.svelte

@ -116,13 +116,17 @@ @@ -116,13 +116,17 @@
<ReferencedEventPreview event={comment} />
{/if}
<div class="comment-header flex items-center gap-2 mb-2">
<div class="comment-header flex items-center gap-2 mb-2 min-w-0">
<div class="flex items-center gap-2 flex-1 min-w-0">
<div class="flex-shrink-0">
<ProfileBadge pubkey={comment.pubkey} />
<span class="text-fog-text-light dark:text-fog-dark-text-light whitespace-nowrap" style="font-size: 0.75em;">{getRelativeTime()}</span>
</div>
<span class="text-fog-text-light dark:text-fog-dark-text-light whitespace-nowrap flex-shrink-0" style="font-size: 0.75em;">{getRelativeTime()}</span>
{#if getClientName()}
<span class="text-fog-text-light dark:text-fog-dark-text-light whitespace-nowrap" style="font-size: 0.75em;">via {getClientName()}</span>
<span class="text-fog-text-light dark:text-fog-dark-text-light whitespace-nowrap flex-shrink-0" style="font-size: 0.75em;">via {getClientName()}</span>
{/if}
<div class="ml-auto flex items-center gap-2 comment-header-actions">
</div>
<div class="flex items-center gap-2 comment-header-actions flex-shrink-0">
<IconButton
icon="eye"
label="View"
@ -202,6 +206,13 @@ @@ -202,6 +206,13 @@
border: 1px solid var(--fog-border, #e5e7eb);
border-radius: 0.25rem;
position: relative;
overflow: hidden;
}
@media (max-width: 640px) {
.comment {
padding: 0.75rem;
}
}
:global(.dark) .comment {
@ -272,13 +283,9 @@ @@ -272,13 +283,9 @@
gap: 0.5rem;
}
.comment-header .ml-auto {
margin-left: auto;
flex-shrink: 0;
}
.comment-header-actions {
gap: 0.375rem;
flex-wrap: wrap;
}
}

7
src/lib/modules/comments/CommentThread.svelte

@ -815,6 +815,13 @@ @@ -815,6 +815,13 @@
padding: 1rem;
}
@media (max-width: 768px) {
.comment-thread {
max-width: 100%;
padding: 0.5rem;
}
}
.comments-list {
margin-bottom: 2rem;
}

33
src/lib/modules/discussions/DiscussionCard.svelte

@ -190,11 +190,11 @@ @@ -190,11 +190,11 @@
{#if !fullView}
<a href="/event/{thread.id}" class="card-link">
<div class="card-content" class:expanded={expanded} bind:this={contentElement}>
<div class="flex justify-between items-start mb-2">
<h3 class="font-semibold text-fog-text dark:text-fog-dark-text">
<div class="flex justify-between items-start mb-2 gap-2">
<h3 class="font-semibold text-fog-text dark:text-fog-dark-text flex-1 min-w-0 overflow-hidden">
{getTitle()}
</h3>
<div class="flex items-center gap-2">
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-fog-text-light dark:text-fog-dark-text-light whitespace-nowrap" style="font-size: 0.875em;">{getRelativeTime()}</span>
<IconButton
icon="eye"
@ -317,16 +317,7 @@ @@ -317,16 +317,7 @@
<!-- Card footer (stats) - always visible, outside collapsible content -->
<div class="flex items-center justify-between text-fog-text dark:text-fog-dark-text thread-stats mt-2" style="font-size: 0.75em;">
<div class="flex items-center gap-4 flex-wrap">
{#if fullView}
<DiscussionVoteButtons event={thread} />
{:else}
<VoteCount
upvotes={providedUpvotes}
downvotes={providedDownvotes}
votesCalculated={providedVotesCalculated}
size="xs"
/>
{/if}
{#if !fullView}
{#if loadingStats}
<span class="text-fog-text-light dark:text-fog-dark-text-light">Loading stats...</span>
@ -373,6 +364,19 @@ @@ -373,6 +364,19 @@
.thread-card {
max-width: var(--content-width);
position: relative;
overflow: hidden;
}
@media (max-width: 768px) {
.thread-card {
max-width: 100%;
}
}
@media (max-width: 640px) {
.thread-card {
padding: 0.75rem;
}
}
.card-link {
@ -470,6 +474,11 @@ @@ -470,6 +474,11 @@
.thread-stats {
margin-bottom: 0.25rem; /* Decreased space between count row and kind badge */
}
h3 {
word-break: break-word;
overflow-wrap: break-word;
}
}
:global(.dark) .kind-badge {

7
src/lib/modules/discussions/DiscussionList.svelte

@ -640,6 +640,13 @@ @@ -640,6 +640,13 @@
padding: 1rem;
}
@media (max-width: 768px) {
.thread-list {
max-width: 100%;
padding: 0.5rem;
}
}
.thread-wrapper {
cursor: pointer;
transition: background 0.2s;

7
src/lib/modules/discussions/DiscussionView.svelte

@ -154,6 +154,13 @@ @@ -154,6 +154,13 @@
padding: 1rem;
}
@media (max-width: 768px) {
.thread-view {
max-width: 100%;
padding: 0.5rem;
}
}
.op-section {
margin-bottom: 2rem;
padding-bottom: 1rem;

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

@ -183,7 +183,7 @@ @@ -183,7 +183,7 @@
// Parse NIP-21 links and create segments for rendering
interface ContentSegment {
type: 'text' | 'profile' | 'event' | 'url' | 'wikilink' | 'hashtag';
type: 'text' | 'profile' | 'event' | 'url' | 'wikilink' | 'hashtag' | 'greentext';
content: string; // Display text (without nostr: prefix for links)
pubkey?: string; // For profile badges
eventId?: string; // For event links (bech32 or hex)
@ -192,6 +192,43 @@ @@ -192,6 +192,43 @@
hashtag?: string; // For hashtag topic name
}
// Process text to detect greentext (lines starting with >)
function processGreentext(text: string): ContentSegment[] {
const lines = text.split('\n');
const segments: ContentSegment[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trimStart();
// Check if line starts with > (greentext)
if (trimmed.startsWith('>') && trimmed.length > 1) {
// Preserve leading whitespace before >
const leadingWhitespace = line.substring(0, line.length - trimmed.length);
segments.push({
type: 'greentext',
content: leadingWhitespace + trimmed
});
} else {
// Regular text line
segments.push({
type: 'text',
content: line
});
}
// Add newline between lines (except for last line)
if (i < lines.length - 1) {
segments.push({
type: 'text',
content: '\n'
});
}
}
return segments;
}
function parseContentWithNIP21Links(): ContentSegment[] {
const plaintext = getPlaintextContent();
const links = findNIP21Links(plaintext);
@ -442,6 +479,20 @@ @@ -442,6 +479,20 @@
}
}
// Process greentext on final text segments (only in feed view)
if (!fullView && finalSegments.length > 0) {
const processedSegments: ContentSegment[] = [];
for (const segment of finalSegments) {
if (segment.type === 'text') {
const greentextSegments = processGreentext(segment.content);
processedSegments.push(...greentextSegments);
} else {
processedSegments.push(segment);
}
}
return processedSegments.length > 0 ? processedSegments : finalSegments;
}
return finalSegments.length > 0 ? finalSegments : segments;
}
@ -782,13 +833,13 @@ @@ -782,13 +833,13 @@
{@const title = getTitle()}
{#if !hideTitle && title && title !== 'Untitled'}
<h2 class="post-title font-bold mb-4 text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;">
<h2 class="post-title font-bold mb-4 text-fog-text dark:text-fog-dark-text overflow-hidden" style="font-size: 1.5em;">
{title}
</h2>
{/if}
<div class="post-header flex items-center justify-between gap-2 mb-2">
<div class="flex items-center gap-2 flex-nowrap flex-1 min-w-0">
<div class="flex items-center gap-2 flex-1 min-w-0 post-header-left">
<div class="flex-shrink-0">
<ProfileBadge pubkey={post.pubkey} />
</div>
@ -848,7 +899,7 @@ @@ -848,7 +899,7 @@
{:else}
<!-- Feed view: plaintext only, no profile pics, media as URLs -->
<div class="post-header flex items-center justify-between gap-2 mb-2">
<div class="flex items-center gap-2 flex-nowrap flex-1 min-w-0">
<div class="flex items-center gap-2 flex-1 min-w-0 post-header-left">
<div class="flex-shrink-0">
<ProfileBadge pubkey={post.pubkey} inline={true} />
</div>
@ -872,7 +923,7 @@ @@ -872,7 +923,7 @@
{@const title = getTitle()}
{#if title && title !== 'Untitled'}
<h2 class="post-title font-bold mb-2 text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;">
<h2 class="post-title font-bold mb-2 text-fog-text dark:text-fog-dark-text overflow-hidden" style="font-size: 1.5em;">
{title}
</h2>
{/if}
@ -904,6 +955,8 @@ @@ -904,6 +955,8 @@
{:else}
{segment.content}
{/if}
{:else if segment.type === 'greentext'}
<span class="greentext">{segment.content}</span>
{:else if segment.type === 'profile' && segment.pubkey}
<ProfileBadge pubkey={segment.pubkey} inline={true} />
{:else if segment.type === 'event' && segment.eventId}
@ -986,6 +1039,11 @@ @@ -986,6 +1039,11 @@
{/if}
{#if !fullView}
<div class="feed-card-actions-section">
<div class="feed-card-reactions">
<FeedReactionButtons event={post} preloadedReactions={preloadedReactions} />
</div>
</div>
<div class="feed-card-footer flex items-center justify-between">
<div class="feed-card-actions flex items-center gap-2">
{#if isLoggedIn && bookmarked}
@ -1051,6 +1109,13 @@ @@ -1051,6 +1109,13 @@
border: 1px solid var(--fog-border, #e5e7eb);
border-radius: 0.25rem;
position: relative;
overflow: hidden;
}
@media (max-width: 640px) {
.Feed-post {
padding: 0.75rem;
}
}
.Feed-post.collapsed {
@ -1150,6 +1215,20 @@ @@ -1150,6 +1215,20 @@
border-top-color: var(--fog-dark-border, #374151);
}
.feed-card-actions-section {
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid var(--fog-border, #e5e7eb);
}
:global(.dark) .feed-card-actions-section {
border-top-color: var(--fog-dark-border, #374151);
}
.feed-card-reactions {
margin-bottom: 0.5rem;
}
.feed-card-footer {
margin-top: 0.5rem;
padding-top: 0.5rem;
@ -1207,6 +1286,34 @@ @@ -1207,6 +1286,34 @@
align-items: center;
line-height: 1.5;
position: relative;
gap: 0.5rem;
min-width: 0;
flex-wrap: wrap;
}
.post-header-left {
min-width: 0;
overflow: hidden;
}
@media (max-width: 640px) {
.post-header {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.post-header-left {
width: 100%;
flex-wrap: wrap;
gap: 0.5rem;
}
.post-header-actions {
width: 100%;
justify-content: flex-start;
flex-wrap: wrap;
}
}
.post-header-divider {
@ -1243,9 +1350,9 @@ @@ -1243,9 +1350,9 @@
flex-wrap: wrap;
}
.post-header {
flex-wrap: wrap;
gap: 0.5rem;
.post-title {
word-break: break-word;
overflow-wrap: break-word;
}
}
@ -1292,6 +1399,14 @@ @@ -1292,6 +1399,14 @@
background-color: rgba(255, 255, 0, 0.2);
}
.greentext {
color: #789922;
}
:global(.dark) .greentext {
color: #8ab378;
}
/* Focusable wrapper for keyboard navigation */
div[role="button"] {
outline: none;

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

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
import ProfileBadge from '../../components/layout/ProfileBadge.svelte';
import MarkdownRenderer from '../../components/content/MarkdownRenderer.svelte';
import EventMenu from '../../components/EventMenu.svelte';
import FeedReactionButtons from '../reactions/FeedReactionButtons.svelte';
import { nostrClient } from '../../services/nostr/nostr-client.js';
import { relayManager } from '../../services/nostr/relay-manager.js';
import { onMount } from 'svelte';
@ -376,6 +377,10 @@ @@ -376,6 +377,10 @@
</div>
{/if}
<div class="highlight-actions">
<FeedReactionButtons event={highlight} />
</div>
<div class="kind-badge">
<span class="kind-number">{getKindInfo(highlight.kind).number}</span>
<span class="kind-description">{getKindInfo(highlight.kind).description}</span>
@ -431,6 +436,17 @@ @@ -431,6 +436,17 @@
border-top-color: var(--fog-dark-border, #374151);
}
.highlight-actions {
padding-top: 0.5rem;
padding-right: 6rem; /* Reserve space for kind badge */
border-top: 1px solid var(--fog-border, #e5e7eb);
margin-top: 0.5rem;
}
:global(.dark) .highlight-actions {
border-top-color: var(--fog-dark-border, #374151);
}
.source-link {
color: var(--fog-accent, #64748b);
text-decoration: none;

1
src/lib/services/nostr/config.ts

@ -14,7 +14,6 @@ const PROFILE_RELAYS = [ @@ -14,7 +14,6 @@ const PROFILE_RELAYS = [
'wss://aggr.nostr.land',
'wss://profiles.nostr1.com',
'wss://relay.primal.net',
'wss://orly-relay.imwald.eu',
'wss://nostr.wine',
'wss://nostr21.com'
];

6
src/routes/discussions/+page.svelte

@ -129,6 +129,12 @@ @@ -129,6 +129,12 @@
margin: 0 auto;
}
@media (max-width: 768px) {
.discussions-content {
max-width: 100%;
}
}
.discussions-header-sticky {
padding: 0 1rem;
padding-top: 1rem;

29
src/routes/feed/+page.svelte

@ -166,8 +166,29 @@ @@ -166,8 +166,29 @@
gap: 1rem;
}
@media (max-width: 640px) {
.feed-controls {
flex-direction: column;
align-items: stretch;
gap: 0.75rem;
}
}
.search-section {
flex: 1;
min-width: 0;
}
@media (max-width: 640px) {
.search-section {
width: 100%;
flex: none;
}
.search-section :global(.unified-search-container) {
max-width: 100%;
width: 100%;
}
}
.feed-header-buttons {
@ -175,6 +196,14 @@ @@ -175,6 +196,14 @@
gap: 0.5rem;
align-items: center;
flex-wrap: wrap;
flex-shrink: 0;
}
@media (max-width: 640px) {
.feed-header-buttons {
width: 100%;
justify-content: flex-start;
}
}
.see-new-events-btn-header {

24
src/routes/topics/+page.svelte

@ -336,6 +336,14 @@ @@ -336,6 +336,14 @@
max-width: var(--content-width);
margin: 0 auto;
padding: 0 1rem;
overflow: hidden;
}
@media (max-width: 768px) {
.topics-page {
max-width: 100%;
padding: 0 0.5rem;
}
}
.filter-section {
@ -351,6 +359,14 @@ @@ -351,6 +359,14 @@
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 0.75rem;
overflow: hidden;
}
@media (max-width: 640px) {
.topics-list {
grid-template-columns: 1fr;
gap: 0.5rem;
}
}
.topics-sentinel {
@ -376,6 +392,14 @@ @@ -376,6 +392,14 @@
cursor: pointer;
transition: all 0.2s;
font-family: monospace;
overflow: hidden;
min-width: 0;
}
@media (max-width: 640px) {
.topic-item {
padding: 0.5rem 0.75rem;
}
}
:global(.dark) .topic-item {

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

@ -230,12 +230,27 @@ @@ -230,12 +230,27 @@
.topic-content {
max-width: var(--content-width);
margin: 0 auto;
overflow: hidden;
}
@media (max-width: 768px) {
.topic-content {
max-width: 100%;
}
}
.topic-header {
padding: 0 1rem;
border-bottom: 1px solid var(--fog-border, #e5e7eb);
padding-bottom: 1rem;
margin-bottom: 1rem;
}
@media (max-width: 640px) {
.topic-header {
padding: 0 0.5rem;
padding-bottom: 0.75rem;
}
}
:global(.dark) .topic-header {
@ -252,6 +267,7 @@ @@ -252,6 +267,7 @@
display: flex;
flex-direction: column;
gap: 1rem;
overflow: hidden;
}
.event-item {
@ -259,6 +275,14 @@ @@ -259,6 +275,14 @@
border: 1px solid var(--fog-border, #e5e7eb);
border-radius: 0.5rem;
background: var(--fog-post, #ffffff);
overflow: hidden;
}
@media (max-width: 640px) {
.event-item {
padding: 0.5rem;
margin: 0 0.5rem;
}
}
:global(.dark) .event-item {

Loading…
Cancel
Save