mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 07:27:23 +02:00
* feat: add protocol-specific actions to chat adapters Extends the chat adapter system with support for slash commands and protocol-specific actions without parameters. New features: - ChatAction type system for defining simple commands - Base adapter getActions() and executeAction() methods - NIP-29 /join and /leave slash commands - Slash command parser for detecting /commands - ChatViewer integration with toast notifications Example usage in NIP-29 groups: /join - Request to join the group /leave - Leave the group The action system is extensible and can be enhanced with parameterized actions (e.g., /kick <user>, /ban <user>) in future iterations. Tests: All 804 tests passing Build: Successful Lint: No errors * feat: add autocomplete for slash commands Extends the chat system with autocomplete UI for slash commands, making actions discoverable and easier to execute. New features: - SlashCommandSuggestionList component with keyboard navigation - Slash command search integrated into MentionEditor - Autocomplete popup shows command name and description - Arrow keys for navigation, Enter to select, Escape to close - Filters available commands based on typed text When typing "/" in chat, users now see: /join Request to join the group /leave Leave the group The autocomplete uses the same TipTap suggestion system as @mentions and :emoji:, providing a consistent UX across all autocomplete features. Tests: All 804 tests passing Build: Successful Lint: 1 warning fixed (unused parameter) * feat: execute slash commands immediately on autocomplete selection When selecting a slash command from the autocomplete menu, the action now executes immediately and clears the input, providing a more streamlined UX. Changes: - Added onCommandExecute prop to MentionEditor - Modified slash command suggestion to call onCommandExecute on selection - Clears editor content immediately after selection - Added handleCommandExecute callback in ChatViewer - Executes action and shows toast notifications Before: Type "/" → select "/join" → press Enter → executes After: Type "/" → select "/join" → executes immediately This matches the expected behavior for command selection - when you choose a command from autocomplete, you want to execute it, not just insert it into the text field. Tests: All 804 tests passing Build: Successful Lint: No new errors * feat: make chat actions context-aware and filter by membership status Actions are now intelligently filtered based on the user's membership in the conversation, providing a cleaner and more intuitive UX. Changes: - Added GetActionsOptions type with conversation and activePubkey - Updated base adapter getActions() to accept optional context - Modified NIP-29 adapter to filter actions based on membership: - /join: only shown when user is NOT a member/admin - /leave: only shown when user IS a member - Updated ChatViewer to pass conversation and user context to searchCommands - Moved searchCommands callback after conversation is defined This prevents showing irrelevant commands like "/join" when you're already a member, or "/leave" when you haven't joined yet. The autocomplete menu now only displays actions that are actually executable in the current context. Implementation notes: - NIP-29 uses getAllActions() fallback when context unavailable - Membership determined by checking conversation.participants array - Other protocols return empty array by default (no actions yet) Tests: All 804 tests passing Build: Successful Lint: No new errors --------- Co-authored-by: Claude <noreply@anthropic.com>
40 lines
796 B
TypeScript
40 lines
796 B
TypeScript
/**
|
|
* Parsed slash command result
|
|
*/
|
|
export interface ParsedSlashCommand {
|
|
/** Command name (without the leading slash) */
|
|
command: string;
|
|
}
|
|
|
|
/**
|
|
* Parse a slash command from message text
|
|
* Returns null if text is not a slash command
|
|
*
|
|
* Examples:
|
|
* "/join" -> { command: "join" }
|
|
* "/leave" -> { command: "leave" }
|
|
* "hello" -> null
|
|
* "not a /command" -> null
|
|
*/
|
|
export function parseSlashCommand(text: string): ParsedSlashCommand | null {
|
|
// Trim whitespace
|
|
const trimmed = text.trim();
|
|
|
|
// Must start with slash
|
|
if (!trimmed.startsWith("/")) {
|
|
return null;
|
|
}
|
|
|
|
// Extract command (everything after the slash)
|
|
const command = trimmed.slice(1).trim();
|
|
|
|
// Must have a command name
|
|
if (!command) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
command,
|
|
};
|
|
}
|