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:
Vivek R
2025-07-07 23:29:49 +05:30
parent a3d7011c43
commit e6662bf0c9
2 changed files with 112 additions and 12 deletions

View File

@@ -197,6 +197,54 @@ fn find_markdown_files(dir: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
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
#[tauri::command]
pub async fn slash_commands_list(
@@ -205,6 +253,9 @@ pub async fn slash_commands_list(
info!("Discovering slash commands");
let mut commands = Vec::new();
// Add default commands
commands.extend(create_default_commands());
// Load project commands if project path is provided
if let Some(proj_path) = project_path {
let project_commands_dir = PathBuf::from(&proj_path).join(".claude").join("commands");

View File

@@ -105,11 +105,11 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
// Filter by active tab
if (activeTab === "default") {
// No default/built-in commands yet
filteredByTab = [];
// Show default/built-in commands
filteredByTab = commands.filter(cmd => cmd.scope === "default");
} else {
// Show all custom commands (both user and project)
filteredByTab = commands;
filteredByTab = commands.filter(cmd => cmd.scope !== "default");
}
// Then filter by search query
@@ -309,15 +309,64 @@ export const SlashCommandPicker: React.FC<SlashCommandPickerProps> = ({
<>
{/* Default Tab Content */}
{activeTab === "default" && (
<div className="flex flex-col items-center justify-center h-full">
<Command className="h-8 w-8 text-muted-foreground mb-2" />
<span className="text-sm text-muted-foreground">
No default commands available
</span>
<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="flex flex-col items-center justify-center h-full">
<Command className="h-8 w-8 text-muted-foreground mb-2" />
<span className="text-sm text-muted-foreground">
{searchQuery ? 'No commands found' : 'No default commands available'}
</span>
{!searchQuery && (
<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 */}