mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-11 16:07:15 +02:00
* 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>
116 lines
2.3 KiB
TypeScript
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 || "";
|
|
}
|