From 3a2acd8500dbec57aa08079446ba50a1d9156e12 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 5 Feb 2026 23:42:09 +0100 Subject: [PATCH] fix green-text formatting implement bookmark page add writing on the wall --- public/healthz.json | 4 +- .../content/MarkdownRenderer.svelte | 72 +++-- src/lib/components/content/MediaViewer.svelte | 6 +- .../content/MentionsAutocomplete.svelte | 302 ++++++++++++++++++ src/lib/components/profile/ProfileMenu.svelte | 82 ++++- .../components/write/CreateEventForm.svelte | 32 +- src/lib/modules/comments/CommentForm.svelte | 53 ++- src/lib/modules/profiles/ProfilePage.svelte | 180 ++++++++++- src/lib/services/file-compression.ts | 137 ++++++++ src/lib/services/mentions.ts | 165 ++++++++++ src/lib/services/nostr/file-upload.ts | 22 +- src/lib/services/profile-search.ts | 184 +++++++++++ src/routes/bookmarks/+page.svelte | 127 +++++++- 13 files changed, 1299 insertions(+), 67 deletions(-) create mode 100644 src/lib/components/content/MentionsAutocomplete.svelte create mode 100644 src/lib/services/file-compression.ts create mode 100644 src/lib/services/mentions.ts create mode 100644 src/lib/services/profile-search.ts diff --git a/public/healthz.json b/public/healthz.json index 5be6279..119d3cb 100644 --- a/public/healthz.json +++ b/public/healthz.json @@ -2,7 +2,7 @@ "status": "ok", "service": "aitherboard", "version": "0.1.1", - "buildTime": "2026-02-05T18:24:55.668Z", + "buildTime": "2026-02-05T22:42:01.031Z", "gitCommit": "unknown", - "timestamp": 1770315895668 + "timestamp": 1770331321031 } \ No newline at end of file diff --git a/src/lib/components/content/MarkdownRenderer.svelte b/src/lib/components/content/MarkdownRenderer.svelte index 901b9b9..472d2dc 100644 --- a/src/lib/components/content/MarkdownRenderer.svelte +++ b/src/lib/components/content/MarkdownRenderer.svelte @@ -331,26 +331,42 @@ } // Convert greentext (>text with no space) to styled spans + // Groups consecutive greentext lines into a single block, preserving line breaks function convertGreentext(text: string): string { - // Split by lines and process each line + // Split by lines and process const lines = text.split('\n'); - const processedLines = lines.map(line => { - // Check if line starts with > followed immediately by non-whitespace (greentext) - // Must match: >text (no space after >) - // Must NOT match: > text (space after >, normal blockquote) - // Also handle HTML-escaped > (>) - const greentextPattern = /^(>|>)([^\s>].*)$/; + const processedLines: string[] = []; + let greentextBlock: string[] = []; + + const greentextPattern = /^(>|>)([^\s>].*)$/; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; const match = line.match(greentextPattern); if (match) { - // This is greentext - wrap in span with greentext class - // Use > character (not >) since we're inserting HTML + // This is greentext - add to current block const greentextContent = escapeHtml(match[2]); - return `>${greentextContent}`; + greentextBlock.push(greentextContent); + } else { + // Not greentext - flush any accumulated greentext block + if (greentextBlock.length > 0) { + // Join with
to preserve line breaks, no extra spacing + const blockContent = greentextBlock.map(content => `>${content}`).join('
'); + processedLines.push(`${blockContent}`); + greentextBlock = []; + } + // Add the non-greentext line as-is + processedLines.push(line); } - - return line; - }); + } + + // Flush any remaining greentext block at the end + if (greentextBlock.length > 0) { + // Join with
to preserve line breaks, no extra spacing + const blockContent = greentextBlock.map(content => `>${content}`).join('
'); + processedLines.push(`${blockContent}`); + } return processedLines.join('\n'); } @@ -507,16 +523,30 @@ }); // Post-process HTML to convert blockquotes that are actually greentext + // Merges consecutive greentext blockquotes into a single block, preserving line breaks function postProcessGreentext(html: string): string { - // Find blockquotes that match greentext pattern (>text with no space) - // These are blockquotes that markdown created from greentext lines - // Pattern:

>text

where there's no space after > - const greentextBlockquotePattern = /]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>/g; + // Pattern to match one or more consecutive greentext blockquotes + // Matches:

>text

(with optional whitespace between) + // where there's no space after > (greentext pattern) + // The (?:...) part matches zero or more additional consecutive blockquotes + const consecutiveGreentextPattern = /(]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>)(\s*]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>)*/g; - return html.replace(greentextBlockquotePattern, (match, content) => { - // Convert to greentext span - const escapedContent = escapeHtml(content); - return `>${escapedContent}`; + return html.replace(consecutiveGreentextPattern, (match) => { + // Extract all greentext contents from the match + const contentPattern = /]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>/g; + const contents: string[] = []; + let contentMatch; + while ((contentMatch = contentPattern.exec(match)) !== null) { + contents.push(contentMatch[1]); + } + + if (contents.length === 0) { + return match; // Shouldn't happen, but safety check + } + + // Join all contents with
to preserve line breaks, no extra spacing + const combinedContent = contents.map(c => escapeHtml(c)).map(content => `>${content}`).join('
'); + return `${combinedContent}`; }); } diff --git a/src/lib/components/content/MediaViewer.svelte b/src/lib/components/content/MediaViewer.svelte index 801d1e9..668081f 100644 --- a/src/lib/components/content/MediaViewer.svelte +++ b/src/lib/components/content/MediaViewer.svelte @@ -45,9 +45,11 @@ {#if mediaType === 'image'} Media {:else if mediaType === 'video'} -