Files
grimoire/src/constants/command-icons.ts
Alejandro 7a293bb41b feat: COUNT (#105)
* Add COUNT command for NIP-45 event counting

Implements the COUNT verb from NIP-45, allowing users to count events
on relays without fetching them. Features:

- New `count` command requiring at least one relay
- Filter-only flags (excludes --limit, --close-on-eose, --view)
- Single relay shows centered count result
- Multiple relays show per-relay breakdown
- Handles approximate counts, errors, and unsupported relays
- Supports $me/$contacts aliases and NIP-05 resolution

Examples:
  count relay.damus.io -k 3 -p npub1...
  count nos.lol relay.damus.io -k 1 -a fiatjaf.com

* Fix $me and $contacts alias resolution in CountViewer

- Fetch contact list (kind 3) using useNostrEvent hook
- Extract contacts from p tags to resolve $contacts alias
- Add "Account Required" message when aliases used without active account
- Match ReqViewer pattern for consistent alias resolution

* Refactor: extract FilterSummaryBadges for compact headers

- Create shared FilterSummaryBadges component (nostr/FilterSummaryBadges.tsx)
- Simplify CountViewer header to single compact line
- Use FilterSummaryBadges in both ReqViewer and CountViewer
- Remove verbose collapsible filter section from CountViewer

* Remove 'events' suffix from count result

* Update count synopsis to show relays can appear anywhere

* Refactor CountViewer to use applesauce-relay pool

Replace manual WebSocket connections with the relay pool's
count() method for NIP-45 COUNT requests. This provides:
- Proper connection reuse via the existing relay pool
- Automatic reconnection handling
- Better integration with the rest of the app

Remove the approximate property since applesauce-relay's
CountResponse type doesn't expose it yet.

* Simplify CountViewer with one-shot requests and compact UI

- Use per-relay count requests with firstValueFrom and timeout
  instead of pool.count() observable that may not complete
- Replace Collapsible-based header with icon-only DropdownMenus
  matching ReqViewer's compact style
- Add raw JSON filter view with syntax highlighting and copy button
- Show relay count and filter in dropdowns instead of expanded sections
- Requests complete after timeout (10s) instead of spinning indefinitely

* Add NIP-45 support detection via NIP-11 relay info

- Check relay's supported_nips via NIP-11 before sending COUNT request
- Return early with "unsupported" status if relay explicitly doesn't support NIP-45
- Differentiate UI between unsupported (yellow Ban icon) and error (red AlertCircle)
- Provide clearer error messages based on whether NIP-11 info was available
- Uses cached relay info when available to avoid redundant requests

* Improve CountViewer header with human-readable filter summary

- Show kinds as badges, authors ("by"), mentions ("@"), hashtags on left
- Move relay status into relay dropdown with per-relay results
- Dropdown shows count per relay, status icons, and error tooltips
- Header now shows "2/3" style relay count trigger with loading state

* Reorder CountViewer header controls and remove redundant mention prefix

- Change control order to: refresh, relays, filter (was: filter, relays, refresh)
- Remove redundant "@" prefix from mentions since UserName with isMention already shows @

* Increase COUNT timeout to 30s and improve window title

- Extend per-relay timeout from 10s to 30s for more reliable results
- Update count window title to show human-readable kind names instead of
  command-line format (e.g., "count: Short Note by abc123..." instead of
  "count -k 1 -a npub...")

* Add spell support for COUNT commands

- Extend spell system to support both REQ and COUNT commands
- Add detectCommandType() to identify command type from string
- Update encodeSpell to use ["cmd", "COUNT"] tag for count commands
- Update decodeSpell to handle COUNT spells
- Update reconstructCommand to accept cmdType parameter
- Add "Save as spell" option to COUNT windows in WindowToolbar
- Update SpellDialog to handle both REQ and COUNT commands

* Add dynamic window title for COUNT with human-readable filter summary

- Add profile fetching for COUNT authors and tagged users
- Add countTitle useMemo with human-readable kind names, authors, mentions, hashtags, and search
- Use same formatting helpers as REQ titles (getKindName, formatProfileNames, etc.)
- Add countTitle to title priority chain after reqTitle
- Title now shows "Short Note • @alice • #bitcoin" instead of "COUNT"

* Update count command documentation for production

- Add note about automatic NIP-11/NIP-45 support detection
- Mention spell saving capability in description

* Add automatic relay selection with NIP-45 filtering for COUNT

- Make relays optional in count-parser (no longer throws if none specified)
- Add useOutboxRelays for automatic relay selection based on filter criteria
- Filter selected relays by NIP-45 support via NIP-11 before querying
- Show "Selecting relays..." and "Filtering by NIP-45..." loading states
- Fall back to aggregator relays if no NIP-45 relays found
- Update man page: relays now optional, new examples showing auto-selection

* Revert automatic relay selection for COUNT command

Simplify COUNT by requiring explicit relay specification:
- Restore relay requirement validation in count-parser.ts
- Remove useOutboxRelays and NIP-45 auto-filtering from CountViewer
- Update man page documentation to reflect required relays
- Keep NIP-45 support detection for better error messages

This keeps the feature simpler for now; automatic relay selection
can be added later when the UX is better understood.

* Reduce padding and sizing in COUNT single result view

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-15 16:13:50 +01:00

116 lines
2.3 KiB
TypeScript

import {
Book,
Podcast,
FileText,
HelpCircle,
List,
BookOpen,
ExternalLink,
User,
Lock,
Unlock,
Radio,
Rss,
Layout,
Bug,
Wifi,
MessageSquare,
Hash,
type LucideIcon,
} from "lucide-react";
/**
* Icon mapping for all commands/apps
* Each command has an icon and optional tooltip description
*/
export interface CommandIcon {
icon: LucideIcon;
description: string;
}
export const COMMAND_ICONS: Record<string, CommandIcon> = {
// Documentation commands
nip: {
icon: Book,
description: "View Nostr Implementation Possibility specification",
},
kind: {
icon: FileText,
description: "View information about a Nostr event kind",
},
kinds: {
icon: List,
description: "Display all supported Nostr event kinds",
},
man: {
icon: BookOpen,
description: "Display manual page for a command",
},
help: {
icon: HelpCircle,
description: "Display general help information",
},
// Nostr commands
req: {
icon: Podcast,
description: "Active subscription to Nostr relays with filters",
},
count: {
icon: Hash,
description: "Count events on relays using NIP-45 COUNT verb",
},
open: {
icon: ExternalLink,
description: "Open and view a Nostr event",
},
profile: {
icon: User,
description: "View a Nostr user profile",
},
relay: {
icon: Radio,
description: "View relay information and statistics",
},
feed: {
icon: Rss,
description: "View event feed",
},
chat: {
icon: MessageSquare,
description: "Join and participate in NIP-29 relay-based group chats",
},
// Utility commands
encode: {
icon: Lock,
description: "Encode data to NIP-19 format",
},
decode: {
icon: Unlock,
description: "Decode NIP-19 encoded identifiers",
},
// System commands
win: {
icon: Layout,
description: "View all open windows",
},
debug: {
icon: Bug,
description: "Display application state for debugging",
},
conn: {
icon: Wifi,
description: "View relay pool connection and authentication status",
},
};
export function getCommandIcon(command: string): LucideIcon {
return COMMAND_ICONS[command]?.icon || FileText;
}
export function getCommandDescription(command: string): string {
return COMMAND_ICONS[command]?.description || "";
}