|
|
import gradio as gr |
|
|
import json |
|
|
import os |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
CATEGORY_COLORS = { |
|
|
'ai-dev': '#8b5cf6', |
|
|
'ai-engineering': '#7c3aed', |
|
|
'claude-code': '#2563eb', |
|
|
'common-tasks': '#059669', |
|
|
'conv-mgmt': '#0891b2', |
|
|
'cybersec': '#dc2626', |
|
|
'development': '#3b82f6', |
|
|
'documentation': '#6366f1', |
|
|
'educational': '#f59e0b', |
|
|
'experiments': '#ec4899', |
|
|
'filesystem-ops': '#84cc16', |
|
|
'for-fun': '#f97316', |
|
|
'general-purpose': '#64748b', |
|
|
'ideation': '#a855f7', |
|
|
'local-ai': '#6366f1', |
|
|
'media': '#06b6d4', |
|
|
'misc': '#9ca3af', |
|
|
'operations': '#10b981', |
|
|
'seo-web': '#14b8a6', |
|
|
'sysadmin': '#22c55e', |
|
|
'tech-docs': '#3b82f6', |
|
|
'writing-and-editing': '#f472b6' |
|
|
} |
|
|
|
|
|
|
|
|
def load_commands(): |
|
|
"""Load slash commands from categorized folder structure""" |
|
|
commands = [] |
|
|
commands_dir = Path("commands") |
|
|
|
|
|
if not commands_dir.exists(): |
|
|
return [] |
|
|
|
|
|
|
|
|
for cmd_file in commands_dir.rglob("*.md"): |
|
|
|
|
|
parts = cmd_file.relative_to(commands_dir).parts |
|
|
category = parts[0] if parts else 'misc' |
|
|
|
|
|
commands.append({ |
|
|
'name': cmd_file.stem, |
|
|
'path': str(cmd_file), |
|
|
'category': category |
|
|
}) |
|
|
|
|
|
|
|
|
commands.sort(key=lambda x: x['name'].lower()) |
|
|
|
|
|
return commands |
|
|
|
|
|
def load_command_content(path): |
|
|
"""Load the content of a specific command file""" |
|
|
file_path = Path(path) |
|
|
if file_path.exists(): |
|
|
with open(file_path, 'r') as f: |
|
|
content = f.read() |
|
|
|
|
|
if content.startswith('---'): |
|
|
parts = content.split('---', 2) |
|
|
if len(parts) >= 3: |
|
|
return parts[2].strip() |
|
|
return content |
|
|
return "Content not found" |
|
|
|
|
|
def search_commands(search_term, commands_data): |
|
|
"""Filter commands based on search term""" |
|
|
if not search_term: |
|
|
return commands_data |
|
|
|
|
|
search_term = search_term.lower() |
|
|
filtered = [ |
|
|
cmd for cmd in commands_data |
|
|
if search_term in cmd['name'].lower() |
|
|
] |
|
|
return filtered |
|
|
|
|
|
def create_command_card(command): |
|
|
"""Create an HTML card for a command""" |
|
|
import html |
|
|
|
|
|
name = command['name'] |
|
|
path = command['path'] |
|
|
category = command.get('category', 'misc') |
|
|
content = load_command_content(path) |
|
|
|
|
|
|
|
|
description = "" |
|
|
lines = content.split('\n') |
|
|
if lines: |
|
|
|
|
|
for line in lines[1:6]: |
|
|
if line.strip() and not line.startswith('#'): |
|
|
description = line.strip() |
|
|
break |
|
|
|
|
|
|
|
|
category_color = CATEGORY_COLORS.get(category, '#9ca3af') |
|
|
category_display = category.replace('-', ' ').title() |
|
|
|
|
|
|
|
|
content_json = json.dumps(content) |
|
|
|
|
|
|
|
|
unique_id = f"cmd-{name.replace(' ', '-').replace('/', '-')}" |
|
|
|
|
|
card_html = f""" |
|
|
<details style="border: 1px solid #e5e7eb; border-radius: 8px; margin: 10px 0; background: white; overflow: hidden;"> |
|
|
<summary style="padding: 16px; cursor: pointer; list-style: none; user-select: none;"> |
|
|
<div style="display: flex; justify-content: space-between; align-items: center; gap: 12px;"> |
|
|
<div style="flex: 1;"> |
|
|
<div style="display: flex; align-items: center; gap: 8px;"> |
|
|
<span style="font-size: 18px; font-weight: 600; color: #1f2937;">/{html.escape(name)}</span> |
|
|
<span style="display: inline-block; padding: 4px 10px; background: {category_color}; color: white; border-radius: 12px; font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px;">{html.escape(category_display)}</span> |
|
|
</div> |
|
|
</div> |
|
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" style="flex-shrink: 0; transition: transform 0.2s;"> |
|
|
<path d="M6 8L10 12L14 8" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> |
|
|
</svg> |
|
|
</div> |
|
|
</summary> |
|
|
<div style="padding: 0 16px 16px 16px; border-top: 1px solid #f3f4f6;"> |
|
|
<div style="position: relative; background: #f9fafb; padding: 16px; border-radius: 6px; margin-top: 12px;"> |
|
|
<button id="copy-btn-{unique_id}" |
|
|
onclick="copyToClipboard{unique_id}()" |
|
|
style="position: absolute; top: 12px; right: 12px; background: #2563eb; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background 0.2s;" |
|
|
onmouseover="this.style.background='#1d4ed8'" |
|
|
onmouseout="this.style.background='#2563eb'"> |
|
|
📋 |
|
|
</button> |
|
|
<script> |
|
|
function copyToClipboard{unique_id}() {{ |
|
|
const text = {content_json}; |
|
|
navigator.clipboard.writeText(text).then(() => {{ |
|
|
const btn = document.getElementById('copy-btn-{unique_id}'); |
|
|
btn.innerHTML = '✓ Copied'; |
|
|
setTimeout(() => {{ |
|
|
btn.innerHTML = '📋'; |
|
|
}}, 1500); |
|
|
}}).catch(err => {{ |
|
|
console.error('Failed to copy:', err); |
|
|
alert('Failed to copy to clipboard'); |
|
|
}}); |
|
|
}} |
|
|
</script> |
|
|
<pre style="margin: 0; white-space: pre-wrap; font-size: 13px; line-height: 1.6; color: #374151; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; padding-right: 60px;">{html.escape(content)}</pre> |
|
|
</div> |
|
|
</div> |
|
|
</details> |
|
|
""" |
|
|
return card_html |
|
|
|
|
|
def display_commands(search_term): |
|
|
"""Main function to display filtered commands""" |
|
|
commands_data = load_commands() |
|
|
filtered_commands = search_commands(search_term, commands_data) |
|
|
|
|
|
if not filtered_commands: |
|
|
return "<p style='text-align: center; color: #666; padding: 40px;'>No commands found matching your search.</p>" |
|
|
|
|
|
html_output = f"<div style='max-width: 900px; margin: 0 auto;'>" |
|
|
html_output += f"<p style='color: #666; margin-bottom: 20px;'>Showing {len(filtered_commands)} command(s)</p>" |
|
|
|
|
|
for command in filtered_commands: |
|
|
html_output += create_command_card(command) |
|
|
|
|
|
html_output += "</div>" |
|
|
return html_output |
|
|
|
|
|
|
|
|
with gr.Blocks( |
|
|
title="Claude Code Slash Commands", |
|
|
theme=gr.themes.Soft(), |
|
|
css=""" |
|
|
.gradio-container { |
|
|
max-width: 1200px !important; |
|
|
} |
|
|
details[open] summary svg { |
|
|
transform: rotate(180deg); |
|
|
} |
|
|
details summary::-webkit-details-marker { |
|
|
display: none; |
|
|
} |
|
|
""" |
|
|
) as demo: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# Claude Code Slash Commands Collection |
|
|
|
|
|
Browse and search through a comprehensive collection of slash commands for Claude Code CLI. |
|
|
Use the search box to filter commands by name, then expand any command to view its full content and copy it to your clipboard. |
|
|
|
|
|
**Source:** [github.com/danielrosehill/Claude-Slash-Commands](https://github.com/danielrosehill/Claude-Slash-Commands) |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
search_box = gr.Textbox( |
|
|
label="Search Commands", |
|
|
placeholder="Type to filter commands (e.g., 'git', 'python', 'setup')...", |
|
|
scale=4 |
|
|
) |
|
|
search_btn = gr.Button("Search", scale=1, variant="primary") |
|
|
|
|
|
commands_display = gr.HTML(value=display_commands("")) |
|
|
|
|
|
|
|
|
search_box.change( |
|
|
fn=display_commands, |
|
|
inputs=[search_box], |
|
|
outputs=[commands_display] |
|
|
) |
|
|
|
|
|
search_btn.click( |
|
|
fn=display_commands, |
|
|
inputs=[search_box], |
|
|
outputs=[commands_display] |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
|
|
|
### About |
|
|
|
|
|
This Space displays slash commands for Claude Code, Anthropic's official CLI tool. |
|
|
Each command is a reusable prompt template that can be used to automate common tasks. |
|
|
|
|
|
**How to use:** |
|
|
1. Search for a command using keywords |
|
|
2. Click to expand and view the full command content |
|
|
3. Click "Copy to Clipboard" to copy the command |
|
|
4. Add it to your `.claude/commands/` directory |
|
|
|
|
|
**Repository:** [danielrosehill/Claude-Slash-Commands](https://github.com/danielrosehill/Claude-Slash-Commands) |
|
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |
|
|
|