mirror of
https://github.com/Z4nzu/hackingtool
synced 2026-05-23 00:49:59 +00:00
Feature: Search tools by name or keyword (/ or s)
- Add _collect_all_tools() — walks all collections recursively and returns (tool_instance, category_name) pairs for 185 tools - Add search_tools() — prompts for query, matches against TITLE and DESCRIPTION (case-insensitive), shows results table with category, user selects a result number to jump directly into tool.show_options() - Wire / and s commands into interact_menu() - Add / search to help overlay and hint bar - Fix ToolManager row number: was hardcoded 18, now computed dynamically from len(categories) + 1 (currently 21)
This commit is contained in:
parent
9b4b5236b2
commit
043fa0e7b7
1 changed files with 83 additions and 2 deletions
|
|
@ -121,6 +121,7 @@ def show_help():
|
|||
(" ─────────────────────────────────────\n", "dim"),
|
||||
(" 1–20 ", "bold cyan"), ("open a category\n", "white"),
|
||||
(" 21 ", "bold cyan"), ("Update / Uninstall hackingtool\n", "white"),
|
||||
(" / or s ", "bold cyan"), ("search tools by name or keyword\n", "white"),
|
||||
(" ? ", "bold cyan"), ("show this help\n", "white"),
|
||||
(" q ", "bold cyan"), ("quit hackingtool\n\n", "white"),
|
||||
(" Inside a category\n", "bold white"),
|
||||
|
|
@ -305,20 +306,96 @@ def build_menu():
|
|||
))
|
||||
|
||||
# ── ToolManager row ──
|
||||
tm_num = len(categories) + 1
|
||||
console.print(
|
||||
f" [bold magenta] 18[/bold magenta] {update_def[1]} "
|
||||
f" [bold magenta] {tm_num}[/bold magenta] {update_def[1]} "
|
||||
f"[magenta]{update_def[2]}[/magenta]"
|
||||
)
|
||||
|
||||
# ── Hint bar ──
|
||||
console.print(Rule(style="dim magenta"))
|
||||
console.print(
|
||||
" [dim]Enter number to open · "
|
||||
" [dim]Enter number · "
|
||||
"[bold cyan]/[/bold cyan] search · "
|
||||
"[bold cyan]?[/bold cyan] help · "
|
||||
"[bold cyan]q[/bold cyan] quit[/dim]\n"
|
||||
)
|
||||
|
||||
|
||||
# ── Search ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _collect_all_tools() -> list[tuple]:
|
||||
"""Walk all collections and return (tool_instance, category_name) pairs."""
|
||||
from core import HackingTool, HackingToolsCollection
|
||||
results = []
|
||||
|
||||
def _walk(items, parent_title=""):
|
||||
for item in items:
|
||||
if isinstance(item, HackingToolsCollection):
|
||||
_walk(item.TOOLS, item.TITLE)
|
||||
elif isinstance(item, HackingTool):
|
||||
results.append((item, parent_title))
|
||||
|
||||
_walk(all_tools)
|
||||
return results
|
||||
|
||||
|
||||
def search_tools():
|
||||
"""Interactive search — user types query, results update, select to jump."""
|
||||
query = Prompt.ask("[bold cyan]/ Search[/bold cyan]", default="").strip().lower()
|
||||
if not query:
|
||||
return
|
||||
|
||||
all_tool_list = _collect_all_tools()
|
||||
|
||||
# Match against title + description
|
||||
matches = []
|
||||
for tool, category in all_tool_list:
|
||||
title = (tool.TITLE or "").lower()
|
||||
desc = (tool.DESCRIPTION or "").lower()
|
||||
if query in title or query in desc:
|
||||
matches.append((tool, category))
|
||||
|
||||
if not matches:
|
||||
console.print(f"[dim]No tools found matching '{query}'[/dim]")
|
||||
Prompt.ask("[dim]Press Enter to return[/dim]", default="")
|
||||
return
|
||||
|
||||
# Display results
|
||||
table = Table(
|
||||
title=f"Search results for '{query}'",
|
||||
box=box.SIMPLE_HEAD, show_lines=True,
|
||||
)
|
||||
table.add_column("No.", justify="center", style="bold cyan", width=5)
|
||||
table.add_column("Tool", style="bold yellow", min_width=20)
|
||||
table.add_column("Category", style="magenta", min_width=15)
|
||||
table.add_column("Description", style="white", overflow="fold")
|
||||
|
||||
for i, (tool, cat) in enumerate(matches, start=1):
|
||||
desc = (tool.DESCRIPTION or "—").splitlines()[0]
|
||||
table.add_row(str(i), tool.TITLE, cat, desc)
|
||||
|
||||
table.add_row("99", "Back to main menu", "", "")
|
||||
console.print(table)
|
||||
|
||||
raw = Prompt.ask("[bold cyan]>[/bold cyan]", default="").strip().lower()
|
||||
if not raw or raw == "99":
|
||||
return
|
||||
|
||||
try:
|
||||
idx = int(raw)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
if 1 <= idx <= len(matches):
|
||||
tool, cat = matches[idx - 1]
|
||||
console.print(Panel(
|
||||
f"[bold magenta]{tool.TITLE}[/bold magenta] [dim]({cat})[/dim]",
|
||||
border_style="magenta", box=box.ROUNDED,
|
||||
))
|
||||
tool.show_options()
|
||||
|
||||
|
||||
# ── Main interaction loop ──────────────────────────────────────────────────────
|
||||
|
||||
def interact_menu():
|
||||
|
|
@ -334,6 +411,10 @@ def interact_menu():
|
|||
show_help()
|
||||
continue
|
||||
|
||||
if raw.startswith("/") or raw in ("s", "search"):
|
||||
search_tools()
|
||||
continue
|
||||
|
||||
if raw in ("q", "quit", "exit"):
|
||||
console.print(Panel(
|
||||
"[bold white on magenta] Goodbye — Come Back Safely [/bold white on magenta]",
|
||||
|
|
|
|||
Loading…
Reference in a new issue