feat: add default slash commands and update UI to display them
- Add built-in slash commands: /add-dir, /init, /review - Update slash_commands_list to include default commands - Enhance SlashCommandPicker UI to show default commands in dedicated tab - Improve empty state handling with search-aware messaging - Add proper command display with icons, descriptions, and scope badges
This commit is contained in:
@@ -197,6 +197,54 @@ fn find_markdown_files(dir: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create default/built-in slash commands
|
||||||
|
fn create_default_commands() -> Vec<SlashCommand> {
|
||||||
|
vec![
|
||||||
|
SlashCommand {
|
||||||
|
id: "default-add-dir".to_string(),
|
||||||
|
name: "add-dir".to_string(),
|
||||||
|
full_command: "/add-dir".to_string(),
|
||||||
|
scope: "default".to_string(),
|
||||||
|
namespace: None,
|
||||||
|
file_path: "".to_string(),
|
||||||
|
content: "Add additional working directories".to_string(),
|
||||||
|
description: Some("Add additional working directories".to_string()),
|
||||||
|
allowed_tools: vec![],
|
||||||
|
has_bash_commands: false,
|
||||||
|
has_file_references: false,
|
||||||
|
accepts_arguments: false,
|
||||||
|
},
|
||||||
|
SlashCommand {
|
||||||
|
id: "default-init".to_string(),
|
||||||
|
name: "init".to_string(),
|
||||||
|
full_command: "/init".to_string(),
|
||||||
|
scope: "default".to_string(),
|
||||||
|
namespace: None,
|
||||||
|
file_path: "".to_string(),
|
||||||
|
content: "Initialize project with CLAUDE.md guide".to_string(),
|
||||||
|
description: Some("Initialize project with CLAUDE.md guide".to_string()),
|
||||||
|
allowed_tools: vec![],
|
||||||
|
has_bash_commands: false,
|
||||||
|
has_file_references: false,
|
||||||
|
accepts_arguments: false,
|
||||||
|
},
|
||||||
|
SlashCommand {
|
||||||
|
id: "default-review".to_string(),
|
||||||
|
name: "review".to_string(),
|
||||||
|
full_command: "/review".to_string(),
|
||||||
|
scope: "default".to_string(),
|
||||||
|
namespace: None,
|
||||||
|
file_path: "".to_string(),
|
||||||
|
content: "Request code review".to_string(),
|
||||||
|
description: Some("Request code review".to_string()),
|
||||||
|
allowed_tools: vec![],
|
||||||
|
has_bash_commands: false,
|
||||||
|
has_file_references: false,
|
||||||
|
accepts_arguments: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/// Discover all custom slash commands
|
/// Discover all custom slash commands
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn slash_commands_list(
|
pub async fn slash_commands_list(
|
||||||
@@ -205,6 +253,9 @@ pub async fn slash_commands_list(
|
|||||||
info!("Discovering slash commands");
|
info!("Discovering slash commands");
|
||||||
let mut commands = Vec::new();
|
let mut commands = Vec::new();
|
||||||
|
|
||||||
|
// Add default commands
|
||||||
|
commands.extend(create_default_commands());
|
||||||
|
|
||||||
// Load project commands if project path is provided
|
// Load project commands if project path is provided
|
||||||
if let Some(proj_path) = project_path {
|
if let Some(proj_path) = project_path {
|
||||||
let project_commands_dir = PathBuf::from(&proj_path).join(".claude").join("commands");
|
let project_commands_dir = PathBuf::from(&proj_path).join(".claude").join("commands");
|
||||||
|
@@ -105,11 +105,11 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
|||||||
|
|
||||||
// Filter by active tab
|
// Filter by active tab
|
||||||
if (activeTab === "default") {
|
if (activeTab === "default") {
|
||||||
// No default/built-in commands yet
|
// Show default/built-in commands
|
||||||
filteredByTab = [];
|
filteredByTab = commands.filter(cmd => cmd.scope === "default");
|
||||||
} else {
|
} else {
|
||||||
// Show all custom commands (both user and project)
|
// Show all custom commands (both user and project)
|
||||||
filteredByTab = commands;
|
filteredByTab = commands.filter(cmd => cmd.scope !== "default");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then filter by search query
|
// Then filter by search query
|
||||||
@@ -309,15 +309,64 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
|
|||||||
<>
|
<>
|
||||||
{/* Default Tab Content */}
|
{/* Default Tab Content */}
|
||||||
{activeTab === "default" && (
|
{activeTab === "default" && (
|
||||||
<div className="flex flex-col items-center justify-center h-full">
|
<>
|
||||||
<Command className="h-8 w-8 text-muted-foreground mb-2" />
|
{filteredCommands.length === 0 && (
|
||||||
<span className="text-sm text-muted-foreground">
|
<div className="flex flex-col items-center justify-center h-full">
|
||||||
No default commands available
|
<Command className="h-8 w-8 text-muted-foreground mb-2" />
|
||||||
</span>
|
<span className="text-sm text-muted-foreground">
|
||||||
<p className="text-xs text-muted-foreground mt-2 text-center px-4">
|
{searchQuery ? 'No commands found' : 'No default commands available'}
|
||||||
Default commands are built-in system commands
|
</span>
|
||||||
</p>
|
{!searchQuery && (
|
||||||
</div>
|
<p className="text-xs text-muted-foreground mt-2 text-center px-4">
|
||||||
|
Default commands are built-in system commands
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{filteredCommands.length > 0 && (
|
||||||
|
<div className="p-2" ref={commandListRef}>
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{filteredCommands.map((command, index) => {
|
||||||
|
const Icon = getCommandIcon(command);
|
||||||
|
const isSelected = index === selectedIndex;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={command.id}
|
||||||
|
data-index={index}
|
||||||
|
onClick={() => handleCommandClick(command)}
|
||||||
|
onMouseEnter={() => setSelectedIndex(index)}
|
||||||
|
className={cn(
|
||||||
|
"w-full flex items-start gap-3 px-3 py-2 rounded-md",
|
||||||
|
"hover:bg-accent transition-colors",
|
||||||
|
"text-left",
|
||||||
|
isSelected && "bg-accent"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4 text-muted-foreground mt-1 flex-shrink-0" />
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-medium">
|
||||||
|
{command.full_command}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground px-1.5 py-0.5 bg-muted rounded">
|
||||||
|
{command.scope}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{command.description && (
|
||||||
|
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
||||||
|
{command.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Custom Tab Content */}
|
{/* Custom Tab Content */}
|
||||||
|
Reference in New Issue
Block a user