This is the persistent context file for Claude Code. Keep it concise and useful.
https://github.com/ayn2op/discordo.githttps://gogs.altsol.dev/claude/discordo-plus.git (branch: master)discordo-plus (installed to /usr/local/bin/)github.com/ayn2op/discordo)github.com/ayn2op/tview (custom tview fork) + tcell/v3arikawa/v3 + ningen/v3 (state management + Discord markdown)goldmark parser + chroma/v2 syntax highlightingBurntSushi/toml, file at ~/.config/discordo/config.toml~/.cache/discordo/ (attachments, logs, state)main.go → cmd/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, reaction_viewer.gointernal/markdown/renderer.go — AST→styled lines, handles Discord markdown flavorsinternal/config/ — config.go (struct + loader), config.toml (defaults, embedded), keybinds.go, theme.go, editor.gointernal/consts/ — app name, cache dirinternal/ui/chat/guildstate.go — persists guild + channel expand/collapse state to ~/.cache/discordo/state.jsontview.LineBuilder → []tview.Line (segments with tcell.Style)style.Url(rawURL) metadata for OSC 8 terminal hyperlinksapp.Suspend() pattern — suspends TUI, runs command, resumesconfig/keybinds.go structs, matched in HandleEvent() via keybind.Matches()ShortHelp() (bottom bar, contextual) and FullHelp() (full overlay via ?)~/.cache/discordo/attachments/, opened via configured viewer or openDefault()▎ bar prefix, wrapped to viewport width, markdown in descriptions//go:embed config.tomlimage_viewer / image_viewer_args config, save_image keybind (S), mpv geometry auto-detection via xdotool, supported types: jpeg/png/webp/gifNewLine() + .Url() style), o in ShortHelp when attachments/URLs present? keybind (was ctrl+.), E edits config in $EDITOR/vim from help overlay~/.cache/discordo/state.json (guildstate.go)h/l panel nav, i focus input, H toggle guilds, c channels picker, I attach file; input isolation (single-char keybinds blocked while typing)~/.config/discordo-plus/, auto-creates dir + default config on first runexec.LookPath validation, atomic writes, restrictive perms, direct exec.Command (no sh -c)url_extractor.go, embed_renderer.go, attachment_handler.go from messages_list.go; replaced open-golang with stdlibdrawReactions), bold for own, real-time gateway updates; e opens emoji picker to add reactions (emoji_picker.go); picker defaults to browse/scroll mode; f toggles favorites (★, up to 10, persisted to ~/.cache/discordo/emoji_favorites.json); v opens reaction viewer popup showing who reacted with each emoji (reaction_viewer.go)/ opens fuzzy search over current channel messages (search_picker.go)T navigates to thread (was t)w shows author info overlay (user_info.go): opens vim-style input, supports :q/:quit/:logout (command_input.go)itemByID evicts stale entries past 500 itemsZ toggles reply collapse (shows > marker only)t toggles timestamps on/off at runtimepickerBrowseHandleKey; global single-char keybinds suppressed while picker is open (fixes i going to message input)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)To add a new site-specific URL label, edit ui.LinkDisplayText() in internal/ui/util.go:
host == "example.com" or strings.HasSuffix(host, ".example.com")) after the existing site blockssegments (pre-split path segments) to extract meaningful parts (e.g., segments[1] for subreddit in /r/{sub})"SiteName - " + segments[1])ui.CdnDisplayName() — encoded URLs and UUIDs become image.ext, long names truncatedhost + truncated path (max 48 chars)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, was t)keybinds.messages_list.user_info — show author info popup (default: w)keybinds.command_mode — open vim-style command input (default: :)keybinds.guilds_tree.arrow_* / keybinds.messages_list.arrow_* — arrow key navigation (default: up/down/left/right)keybinds.focus_previous / keybinds.focus_next — vim-style panel nav (default: h/l)keybinds.focus_message_input — focus input (default: i)keybinds.toggle_guilds_tree — toggle sidebar (default: H)keybinds.toggle_channels_picker — channels picker (default: c)keybinds.attach_file — global attach file keybind (default: I)keybinds.messages_list.reply / reply_mention — swapped: r = reply (no mention), R = @replykeybinds.guilds_tree.yank_id / keybinds.messages_list.yank_id — copy ID (default: C, was i)keybinds.messages_list.toggle_timestamps — runtime timestamp toggle (default: t)keybinds.messages_list.toggle_replies — collapse/expand reply quotes (default: Z)keybinds.messages_list.add_reaction — open emoji picker (default: e)keybinds.messages_list.view_reactions — view who reacted popup (default: v)go build -o discordo-plus .sudo mv discordo-plus /usr/local/bin/makepkg -si (uses PKGBUILD, installs via pacman)discordo-plusgo test ./... (only config + keyring packages have tests)mpv (or configured image viewer), xdotool (optional, X11 geometry detection), xdg-open (Linux, for default opener)claude <claude@altsol.dev>type(scope): description (e.g., feat(ui/chat): add image viewer)master./research/: SECFILE.md, COMPLIANCE.md, TECHFILE.mdxdotool geometry detection only works on X11; on Wayland use image_viewer_args or compositor window rules for mpv positioningviewerArgs() only adds special flags for mpv; other viewers get plain viewer path invocation (use image_viewer_args to customize)