From 17954ef4b77e4bbcd4a78a48920081f0dd57f7e1 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 9 Feb 2026 22:50:50 +0100 Subject: [PATCH] bug-fixes --- .dockerignore | 47 +++++++ Dockerfile | 22 ++- docker-compose.yml | 11 +- docker-entrypoint.sh | 17 ++- httpd.conf.template | 1 + src/app.css | 6 +- src/lib/components/layout/ProfileBadge.svelte | 21 ++- src/lib/modules/comments/Comment.svelte | 31 +++-- src/lib/modules/comments/CommentThread.svelte | 7 + .../modules/discussions/DiscussionCard.svelte | 35 +++-- .../modules/discussions/DiscussionList.svelte | 7 + .../modules/discussions/DiscussionView.svelte | 7 + src/lib/modules/feed/FeedPost.svelte | 131 ++++++++++++++++-- src/lib/modules/feed/HighlightCard.svelte | 16 +++ src/lib/services/nostr/config.ts | 1 - src/routes/discussions/+page.svelte | 6 + src/routes/feed/+page.svelte | 29 ++++ src/routes/topics/+page.svelte | 24 ++++ src/routes/topics/[name]/+page.svelte | 24 ++++ 19 files changed, 388 insertions(+), 55 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..aae7ec5 --- /dev/null +++ b/.dockerignore @@ -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 diff --git a/Dockerfile b/Dockerfile index eb4ca2e..d7a4734 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 && \ 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 diff --git a/docker-compose.yml b/docker-compose.yml index a4c7413..41a0908 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 4283ae0..7a049f0 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -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 - echo "Port $PORT appears to be available" +# 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 + 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 "WARNING: Port $PORT might be in use" + echo "Port check skipped (ss/netstat not available)" fi echo "Starting Apache on port $PORT..." diff --git a/httpd.conf.template b/httpd.conf.template index e6232e9..093f561 100644 --- a/httpd.conf.template +++ b/httpd.conf.template @@ -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" diff --git a/src/app.css b/src/app.css index 5fe0e1b..ce35304 100644 --- a/src/app.css +++ b/src/app.css @@ -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'] { diff --git a/src/lib/components/layout/ProfileBadge.svelte b/src/lib/components/layout/ProfileBadge.svelte index 18f0bfd..407452a 100644 --- a/src/lib/components/layout/ProfileBadge.svelte +++ b/src/lib/components/layout/ProfileBadge.svelte @@ -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 @@ {/if} {#if !pictureOnly}
-
+
{profile?.name || shortenedNpub} {#if profile?.nip05 && profile.nip05.length > 0} - + {profile.nip05[0]} {/if} @@ -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; + } } diff --git a/src/lib/modules/comments/Comment.svelte b/src/lib/modules/comments/Comment.svelte index 29ce1b7..a067cd8 100644 --- a/src/lib/modules/comments/Comment.svelte +++ b/src/lib/modules/comments/Comment.svelte @@ -116,13 +116,17 @@ {/if} -
- - {getRelativeTime()} - {#if getClientName()} - via {getClientName()} - {/if} -
+
+
+
+ +
+ {getRelativeTime()} + {#if getClientName()} + via {getClientName()} + {/if} +
+
-
-

+
+

{getTitle()}

-
+
{getRelativeTime()}
- {#if fullView} - - {:else} - - {/if} + {#if !fullView} {#if loadingStats} Loading stats... @@ -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 @@ .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 { diff --git a/src/lib/modules/discussions/DiscussionList.svelte b/src/lib/modules/discussions/DiscussionList.svelte index 24130a3..dffef14 100644 --- a/src/lib/modules/discussions/DiscussionList.svelte +++ b/src/lib/modules/discussions/DiscussionList.svelte @@ -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; diff --git a/src/lib/modules/discussions/DiscussionView.svelte b/src/lib/modules/discussions/DiscussionView.svelte index 1d84653..331f6f9 100644 --- a/src/lib/modules/discussions/DiscussionView.svelte +++ b/src/lib/modules/discussions/DiscussionView.svelte @@ -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; diff --git a/src/lib/modules/feed/FeedPost.svelte b/src/lib/modules/feed/FeedPost.svelte index c2a457c..e8481bc 100644 --- a/src/lib/modules/feed/FeedPost.svelte +++ b/src/lib/modules/feed/FeedPost.svelte @@ -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 @@ 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 @@ } } + // 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 @@ {@const title = getTitle()} {#if !hideTitle && title && title !== 'Untitled'} -

+

{title}

{/if}
-
+
@@ -848,7 +899,7 @@ {:else}
-
+
@@ -872,7 +923,7 @@ {@const title = getTitle()} {#if title && title !== 'Untitled'} -

+

{title}

{/if} @@ -904,6 +955,8 @@ {:else} {segment.content} {/if} + {:else if segment.type === 'greentext'} + {segment.content} {:else if segment.type === 'profile' && segment.pubkey} {:else if segment.type === 'event' && segment.eventId} @@ -986,6 +1039,11 @@ {/if} {#if !fullView} +
+
+ +
+