Ver Fonte

feat(ui/chat): clean attachment filenames, suppress link preview embeds

- Apply CdnDisplayName to attachment filenames so encoded-URL and UUID
  filenames show as "image.ext" instead of garbled strings
- Skip link preview embeds (no description/fields/footer) when the
  embed URL already appears in message content (fixes Tenor duplicates)
- Remove show_attachment_links config (now unused)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
claude há 1 mês atrás
pai
commit
89512fdb7c
5 ficheiros alterados com 22 adições e 6 exclusões
  1. 3 1
      CLAUDE.md
  2. 1 1
      README.md
  3. 7 0
      internal/ui/chat/embed_renderer.go
  4. 8 1
      internal/ui/chat/messages_list.go
  5. 3 3
      internal/ui/util.go

+ 3 - 1
CLAUDE.md

@@ -61,7 +61,7 @@ This is the persistent context file for Claude Code. Keep it concise and useful.
 - **LRU cache cap**: `itemByID` evicts stale entries past 500 items
 - **Reply quote italic**: dim + italic style for reply lines
 - **Picker browse mode**: ESC in overlay pickers enters browse mode (j/k/g/G/i) via `pickerBrowseHandleKey`
-- **Link display compression**: `ui.LinkDisplayText()` shows human-friendly labels instead of raw URLs in chat and embeds (e.g., CDN filename, "Tenor GIF", "Substack - author")
+- **Link display compression**: `ui.LinkDisplayText()` shows human-friendly labels instead of raw URLs in chat and embeds; `ui.CdnDisplayName()` cleans attachment filenames (encoded URLs → `image.ext`, UUIDs → `image.ext`); link preview embeds suppressed when URL already in message content (`isLinkPreviewEmbed`); removed `show_attachment_links` config (attachments always show as OSC 8 clickable filenames)
 
 ## Adding Link Display Rules
 To add a new site-specific URL label, edit `ui.LinkDisplayText()` in `internal/ui/util.go`:
@@ -69,6 +69,8 @@ To add a new site-specific URL label, edit `ui.LinkDisplayText()` in `internal/u
 2. Use `segments` (pre-split path segments) to extract meaningful parts (e.g., `segments[1]` for subreddit in `/r/{sub}`)
 3. Return a short label string (e.g., `"SiteName - " + segments[1]`)
 4. The raw URL is preserved as OSC 8 hyperlink metadata — only the visible text changes
+- CDN filenames also cleaned via `ui.CdnDisplayName()` — encoded URLs and UUIDs become `image.ext`, long names truncated
+- Link preview embeds (no description/fields/footer) are skipped when the URL is already in message content
 - Currently handled: Discord CDN (filename), Tenor (GIF), Substack (author), YouTube, X/Twitter, Reddit (subreddit), GitHub (owner/repo)
 - Fallback: `host + truncated path` (max 48 chars)
 

+ 1 - 1
README.md

@@ -7,7 +7,7 @@ A fork of [discordo](https://github.com/ayn2op/discordo) — a lightweight Disco
 ### Media & Attachments
 - **Image viewer**: Opens image attachments (jpeg, png, webp, gif) in a configurable viewer (`mpv` by default) with X11 geometry auto-detection
 - **Save image**: `S` saves the selected message's image attachment to a configurable directory
-- **Link display compression**: Long URLs are shown as human-friendly labels — Discord CDN links show the filename, known sites show short labels (e.g., "Tenor GIF", "YouTube", "GitHub - owner/repo"), others show host + truncated path. Full URL remains clickable via OSC 8 hyperlinks.
+- **Link display compression**: URLs are shown as human-friendly labels — Discord CDN links show the filename, known sites show short labels (e.g., "Tenor GIF", "YouTube", "GitHub - owner/repo"), others show host + truncated path. Garbled CDN filenames (URL-encoded URLs, UUIDs) are cleaned up. Redundant link preview embeds are suppressed. Full URLs remain clickable via OSC 8 hyperlinks.
 
 ### Navigation & UI
 - **ESC focus cycling**: ESC cycles focus between input → messages → guilds → input; arrow keys navigate between panels

+ 7 - 0
internal/ui/chat/embed_renderer.go

@@ -125,6 +125,13 @@ func embedLines(embed discord.Embed, contentURLs map[string]struct{}) []embedLin
 	return lines
 }
 
+// isLinkPreviewEmbed returns true if the embed is a simple link preview
+// with no substantial content beyond provider/title/thumbnail.
+func isLinkPreviewEmbed(embed discord.Embed) bool {
+	return embed.Description == "" && len(embed.Fields) == 0 &&
+		(embed.Footer == nil || embed.Footer.Text == "")
+}
+
 func unescapeMarkdownEscapes(s string) string {
 	if !strings.ContainsRune(s, '\\') {
 		return s

+ 8 - 1
internal/ui/chat/messages_list.go

@@ -501,7 +501,7 @@ func (ml *messagesList) drawDefaultMessage(builder *tview.LineBuilder, message d
 	attachmentStyle := ui.MergeStyle(baseStyle, ml.cfg.Theme.MessagesList.AttachmentStyle.Style)
 	for _, a := range message.Attachments {
 		builder.NewLine()
-		builder.Write(a.Filename, attachmentStyle.Url(a.URL))
+		builder.Write(ui.CdnDisplayName(a.Filename), attachmentStyle.Url(a.URL))
 	}
 
 	// Thread indicator
@@ -562,6 +562,13 @@ func (ml *messagesList) drawEmbeds(builder *tview.LineBuilder, message discord.M
 	wrapWidth := max(innerWidth-prefixWidth, 1)
 
 	for _, embed := range message.Embeds {
+		// Skip embeds that are just link previews for URLs already shown in message content.
+		if embed.URL != "" {
+			if _, inContent := contentURLs[string(embed.URL)]; inContent && isLinkPreviewEmbed(embed) {
+				continue
+			}
+		}
+
 		lines := embedLines(embed, contentURLs)
 		if len(lines) == 0 {
 			continue

+ 3 - 3
internal/ui/util.go

@@ -149,7 +149,7 @@ func LinkDisplayText(raw string) string {
 	// Discord CDN / media — show cleaned filename
 	if host == "cdn.discordapp.com" || host == "media.discordapp.net" {
 		if base := path.Base(p); base != "" && base != "." && base != "/" {
-			return cdnDisplayName(base)
+			return CdnDisplayName(base)
 		}
 	}
 
@@ -206,9 +206,9 @@ func LinkDisplayText(raw string) string {
 	}
 }
 
-// cdnDisplayName cleans up Discord CDN filenames for display.
+// CdnDisplayName cleans up Discord CDN filenames for display.
 // Handles URL-encoded URLs used as filenames and UUID filenames.
-func cdnDisplayName(name string) string {
+func CdnDisplayName(name string) string {
 	ext := path.Ext(name)
 	stem := strings.TrimSuffix(name, ext)