CLAUDE.md 7.4 KB

Project Context

What This File Is

This is the persistent context file for Claude Code. Keep it concise and useful.

CLAUDE.md Maintenance Rules

  • Keep this file under 160 lines
  • Only record decisions, not explanations of common knowledge
  • When adding something, check if anything existing is now outdated and remove it
  • Use short bullet points, not paragraphs
  • No boilerplate or filler text
  • If a section grows past 10 items, consolidate or prune the least relevant ones
  • Format: what we chose + why in one line (e.g., "imv over feh — WebP/Wayland support")

Overview

  • Fork of ayn2op/discordo — a Discord TUI client
  • Upstream: https://github.com/ayn2op/discordo.git
  • Our repo: https://gogs.altsol.dev/claude/discordo-plus.git (branch: master)
  • Binary name: discordo-plus (installed to /usr/local/bin/)

Stack

  • Go (module: github.com/ayn2op/discordo)
  • TUI: github.com/ayn2op/tview (custom tview fork) + tcell/v3
  • Discord API: arikawa/v3 + ningen/v3 (state management + Discord markdown)
  • Markdown: goldmark parser + chroma/v2 syntax highlighting
  • Config: TOML via BurntSushi/toml, file at ~/.config/discordo/config.toml
  • Cache: ~/.cache/discordo/ (attachments, logs, state)

Architecture

  • main.gocmd/root.go (app init) → internal/ui/ (TUI layers)
  • internal/ui/chat/ — core chat UI: messages_list.go (rendering/keybinds), attachment_handler.go, embed_renderer.go, url_extractor.go, message_input.go, guilds_tree.go, attachments_picker.go
  • internal/markdown/renderer.go — AST→styled lines, handles Discord markdown flavors
  • internal/config/config.go (struct + loader), config.toml (defaults, embedded), keybinds.go, theme.go, editor.go
  • internal/consts/ — app name, cache dir
  • internal/ui/chat/guildstate.go — persists guild expand/collapse state to ~/.cache/discordo/state.json
  • Rendering pipeline: messages → tview.LineBuilder[]tview.Line (segments with tcell.Style)
  • URLs get style.Url(rawURL) metadata for OSC 8 terminal hyperlinks
  • External commands (editor, image viewer) use app.Suspend() pattern — suspends TUI, runs command, resumes

Key Patterns

  • Keybinds: defined in config/keybinds.go structs, matched in HandleEvent() via keybind.Matches()
  • Help tooltips: ShortHelp() (bottom bar, contextual) and FullHelp() (full overlay via ?)
  • Attachments: downloaded to ~/.cache/discordo/attachments/, opened via configured viewer or openDefault()
  • Embeds: rendered with bar prefix, wrapped to viewport width, markdown in descriptions
  • Config defaults embedded via //go:embed config.toml

Our Changes (vs upstream)

  • Image viewer: image_viewer config (default: mpv) — opens image attachments in configured viewer with TUI suspend/resume instead of browser
  • mpv geometry: viewerArgs() auto-detects terminal geometry via xdotool on X11, passes --geometry, --force-window, --loop-file=inf to mpv
  • Save image: save_image keybind (S) — saves image attachment to image_save_dir
  • Attachment URL fix: split builder.Write(filename+"\n"+url) into proper NewLine() + .Url() style
  • Supported image types: jpeg, png, webp, gif (via supportedImageTypes map)
  • ShortHelp: o (open) now shows in bottom tooltip bar when message has attachments/URLs
  • Help keybind: changed from ctrl+. to ?ctrl+. not reliably handled by terminals
  • Edit config: E keybind (only active in help overlay) opens config in $EDITOR/vim via app.Suspend()
  • Editor default: falls back to vim when editor = "default" and $EDITOR is unset
  • Guild state persistence: expanded/collapsed guild state saved to ~/.cache/discordo/state.json, restored on launch via guildstate.go
  • Focus on channel select: AutoFocus now targets messages list instead of message input when selecting a channel
  • Security hardening: path traversal prevention (filepath.Base), HTTPS-only downloads, bounded downloads (100MB), exec.LookPath viewer validation, atomic guild state writes, restrictive file permissions (0700/0600), env token warning
  • Editor security: replaced sh -c with direct exec.Command + strings.Fields (merged editor files into single editor.go, no build tags)
  • God file split: extracted url_extractor.go, embed_renderer.go, attachment_handler.go from messages_list.go
  • Image viewer args: image_viewer_args config field — explicit viewer args, bypasses xdotool auto-detection (Wayland-friendly)
  • Brotli body leak fix: transport.go properly closes underlying HTTP body
  • Cache panic fix: cache.Get() uses comma-ok assertion instead of bare type assert
  • Reactions display: renders message reactions below content (drawReactions), bold for own reactions, real-time updates via gateway events
  • Search picker: / keybind opens fuzzy search over current channel messages (search_picker.go)
  • Thread indicators: shows "Thread: name" on messages with threads, t keybind navigates to thread
  • User info popup: w keybind shows author info (username, join date, roles, color) in overlay (user_info.go)
  • Command mode: : keybind opens vim-style command input, supports :q/:quit/:logout (command_input.go)
  • LRU cache cap: itemByID map evicts stale entries when exceeding 500 items (COMP #17)
  • Replace open-golang: replaced skratchdot/open-golang with stdlib os/exec + runtime.GOOS (TECH #1)

Config Fields We Added

  • image_viewer — external image viewer command (default: "mpv", "default" = system opener)
  • image_viewer_args — explicit viewer args list, overrides auto-detection (default: [], Wayland-friendly)
  • image_save_dir — directory for saved images (supports ~/, default: current dir)
  • keybinds.messages_list.save_image — save image keybind (default: S)
  • keybinds.edit_config — open config in editor from help overlay (default: E)
  • keybinds.toggle_help — changed default from ctrl+. to ?
  • keybinds.messages_list.search — fuzzy search messages (default: /)
  • keybinds.messages_list.open_thread — navigate to message's thread (default: t)
  • keybinds.messages_list.user_info — show author info popup (default: w)
  • keybinds.command_mode — open vim-style command input (default: :)

Build & Run

  • Build: go build -o discordo-plus .
  • Install: sudo mv discordo-plus /usr/local/bin/
  • Run: discordo-plus
  • Test: go test ./... (only config + keyring packages have tests)
  • Dependencies: mpv (or configured image viewer), xdotool (optional, X11 geometry detection), xdg-open (Linux, for default opener)

Git

  • Identity: claude <claude@altsol.dev>
  • Commit style: type(scope): description (e.g., feat(ui/chat): add image viewer)
  • Branch: master

Audit Status

  • Research audits in ./research/: SECFILE.md, COMPLIANCE.md, TECHFILE.md
  • Resolved all low-risk findings (marked ✅ FIXED in research files)
  • Remaining unfixed: SEC #7 (raw events in debug), COMP #19-20 (perf), COMP #22/24 (linter/tests), TECH #2-3 (unmaintained deps)

Known Issues

  • Discord ToS discourages third-party clients — use at own risk
  • xdotool geometry detection only works on X11; on Wayland use image_viewer_args or compositor window rules for mpv positioning
  • viewerArgs() only adds special flags for mpv; other viewers get plain viewer path invocation (use image_viewer_args to customize)