mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-11 07:56:50 +02:00
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
This commit is contained in:
@@ -18,6 +18,7 @@ import { Nip29Adapter } from "@/lib/chat/adapters/nip-29-adapter";
|
||||
import { Nip53Adapter } from "@/lib/chat/adapters/nip-53-adapter";
|
||||
import type { ChatProtocolAdapter } from "@/lib/chat/adapters/base-adapter";
|
||||
import type { Message } from "@/types/chat";
|
||||
import type { ChatAction } from "@/types/chat-actions";
|
||||
import { parseSlashCommand } from "@/lib/chat/slash-command-parser";
|
||||
import { UserName } from "./nostr/UserName";
|
||||
import { RichText } from "./nostr/RichText";
|
||||
@@ -497,6 +498,36 @@ export function ChatViewer({
|
||||
}
|
||||
};
|
||||
|
||||
// Handle command execution from autocomplete
|
||||
const handleCommandExecute = useCallback(
|
||||
async (action: ChatAction) => {
|
||||
if (!conversation || !hasActiveAccount || isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
const result = await adapter.executeAction(action.name, {
|
||||
activePubkey: activeAccount.pubkey,
|
||||
activeSigner: activeAccount.signer,
|
||||
conversation,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Action completed");
|
||||
} else {
|
||||
toast.error(result.message || "Action failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Chat] Failed to execute action:", error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Action failed";
|
||||
toast.error(errorMessage);
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
},
|
||||
[conversation, hasActiveAccount, isSending, adapter, activeAccount],
|
||||
);
|
||||
|
||||
// Handle reply button click
|
||||
const handleReply = useCallback((messageId: string) => {
|
||||
setReplyTo(messageId);
|
||||
@@ -813,6 +844,7 @@ export function ChatViewer({
|
||||
searchProfiles={searchProfiles}
|
||||
searchEmojis={searchEmojis}
|
||||
searchCommands={searchCommands}
|
||||
onCommandExecute={handleCommandExecute}
|
||||
onSubmit={(content, emojiTags) => {
|
||||
if (content.trim()) {
|
||||
handleSend(content, replyTo, emojiTags);
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface MentionEditorProps {
|
||||
searchProfiles: (query: string) => Promise<ProfileSearchResult[]>;
|
||||
searchEmojis?: (query: string) => Promise<EmojiSearchResult[]>;
|
||||
searchCommands?: (query: string) => Promise<ChatAction[]>;
|
||||
onCommandExecute?: (action: ChatAction) => Promise<void>;
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
@@ -159,6 +160,7 @@ export const MentionEditor = forwardRef<
|
||||
searchProfiles,
|
||||
searchEmojis,
|
||||
searchCommands,
|
||||
onCommandExecute,
|
||||
autoFocus = false,
|
||||
className = "",
|
||||
},
|
||||
@@ -620,15 +622,17 @@ export const MentionEditor = forwardRef<
|
||||
...slashCommandSuggestion,
|
||||
command: ({ editor, props }: any) => {
|
||||
// props is the ChatAction
|
||||
// Replace the entire content with just the command
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange({ from: 0, to: editor.state.doc.content.size })
|
||||
.insertContentAt(0, [
|
||||
{ type: "text", text: `/${props.name}` },
|
||||
])
|
||||
.run();
|
||||
// Execute the command immediately and clear the editor
|
||||
editor.commands.clearContent();
|
||||
if (onCommandExecute) {
|
||||
// Execute action asynchronously
|
||||
onCommandExecute(props).catch((error) => {
|
||||
console.error(
|
||||
"[MentionEditor] Command execution failed:",
|
||||
error,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
renderLabel({ node }) {
|
||||
@@ -643,6 +647,7 @@ export const MentionEditor = forwardRef<
|
||||
mentionSuggestion,
|
||||
emojiSuggestion,
|
||||
slashCommandSuggestion,
|
||||
onCommandExecute,
|
||||
placeholder,
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user