Files
browser-cli/browser_cli/commands/groups.py
T
daniel156161 61b774a7a4
Package Extension / package-extension (push) Successful in 12s
Build & Publish Package / publish (push) Successful in 22s
add multi browser mode to arragate data from all browsers by tabs list, tabs count, group list, group count and windows list
remove (unnamed) into the group names just leave it a empty string, remove Focused on windows how should the browser know what windows are focused
2026-04-10 12:49:51 +02:00

175 lines
5.8 KiB
Python

import click
from browser_cli.client import BrowserNotConnected, active_browser_targets, send_command
from rich.console import Console
from rich.table import Table
console = Console()
def _handle(command, args=None, profile=None):
try:
return send_command(command, args or {}, profile=profile)
except BrowserNotConnected as e:
console.print(f"[red]Error:[/red] {e}")
raise SystemExit(1)
except RuntimeError as e:
console.print(f"[red]Browser error:[/red] {e}")
raise SystemExit(1)
def _handle_multi(command, args=None, profile=None):
try:
return send_command(command, args or {}, profile=profile)
except (BrowserNotConnected, RuntimeError):
return None
def _multi_browser_targets():
root = click.get_current_context().find_root()
if root.obj.get("browser_explicit"):
return []
targets = active_browser_targets()
if len(targets) <= 1:
return []
return targets
def _print_groups(groups: list[dict], *, show_browser: bool = False) -> None:
if not groups:
console.print("[yellow]No groups found[/yellow]")
return
table = Table(show_header=True, header_style="bold cyan")
if show_browser:
table.add_column("Browser")
table.add_column("ID", style="dim", no_wrap=True)
table.add_column("Name")
table.add_column("Color", width=10)
table.add_column("Collapsed", width=10)
table.add_column("Tabs", width=6)
for g in groups:
row = [
g.get("browser", "") if show_browser else None,
str(g.get("id", "")),
g.get("title") or "",
g.get("color") or "",
"yes" if g.get("collapsed") else "no",
str(g.get("tabCount", "")),
]
table.add_row(*[value for value in row if value is not None])
console.print(table)
@click.group("group")
def group_group():
"""Manage tab groups."""
@group_group.command("list")
def group_list():
"""List all tab groups."""
targets = _multi_browser_targets()
if targets:
groups = []
for target in targets:
result = _handle_multi("group.list", profile=target.profile)
if result is None:
continue
groups.extend({**group, "browser": target.display_name} for group in result)
if not groups:
console.print("[red]Error:[/red] Cannot resolve a browser socket automatically.")
raise SystemExit(1)
_print_groups(groups, show_browser=True)
return
groups = _handle("group.list")
_print_groups(groups or [])
@group_group.command("tabs")
@click.argument("group_id", type=int)
def group_tabs(group_id):
"""List tabs inside a group."""
from browser_cli.commands.tabs import _print_tabs
tabs = _handle("group.tabs", {"groupId": group_id})
_print_tabs(tabs or [])
@group_group.command("count")
def group_count():
"""Count all tab groups."""
targets = _multi_browser_targets()
if targets:
table = Table(show_header=True, header_style="bold cyan")
table.add_column("Browser")
table.add_column("Groups", justify="right")
total = 0
rows = 0
for target in targets:
count = _handle_multi("group.count", profile=target.profile)
if count is None:
continue
count = int(count or 0)
total += count
rows += 1
table.add_row(target.display_name, str(count))
if rows == 0:
console.print("[red]Error:[/red] Cannot resolve a browser socket automatically.")
raise SystemExit(1)
table.add_row("Total", str(total))
console.print(table)
return
count = _handle("group.count")
console.print(f"[bold]{count}[/bold] group(s)")
@group_group.command("query")
@click.argument("search")
def group_query(search):
"""Search groups by name."""
groups = _handle("group.query", {"search": search})
_print_groups(groups or [])
@group_group.command("close")
@click.argument("group_id", type=int)
def group_close(group_id):
"""Close (ungroup and optionally close) a tab group."""
_handle("group.close", {"groupId": group_id})
console.print(f"[green]Group {group_id} closed[/green]")
@group_group.command("create")
@click.argument("name")
def group_create(name):
"""Create a new tab group with NAME."""
result = _handle("group.open", {"name": name})
gid = result.get("id") if isinstance(result, dict) else result
console.print(f"[green]Created group '{name}'[/green] (id: {gid})")
@group_group.command("add-tab")
@click.argument("group")
@click.argument("url", required=False)
def group_add_tab(group, url):
"""Open a new tab (optionally at URL) inside GROUP (name or ID)."""
result = _handle("group.add_tab", {"group": group, "url": url})
tab_id = result.get("tabId") if isinstance(result, dict) else result
label = url or "new tab"
console.print(f"[green]Opened {label}[/green] in group '{group}' (tab id: {tab_id})")
@group_group.command("move")
@click.argument("group")
@click.option("--forward", is_flag=True, help="Move group one position to the right")
@click.option("--backward", is_flag=True, help="Move group one position to the left")
def group_move(group, forward, backward):
"""Move a tab group forward or backward (name or ID)."""
if not forward and not backward:
console.print("[red]Specify --forward or --backward[/red]")
raise SystemExit(1)
result = _handle("group.move", {"group": group, "forward": forward, "backward": backward})
if isinstance(result, dict) and not result.get("moved"):
console.print(f"[yellow]Group '{group}' is already at the {'end' if forward else 'start'}[/yellow]")
else:
direction = "forward" if forward else "backward"
console.print(f"[green]Group '{group}' moved {direction}[/green]")