update python module with new commands and new classes
This commit is contained in:
+180
-79
@@ -4,165 +4,266 @@ browser_cli — Python API for controlling your running browser.
|
|||||||
Usage:
|
Usage:
|
||||||
from browser_cli import BrowserCLI
|
from browser_cli import BrowserCLI
|
||||||
b = BrowserCLI()
|
b = BrowserCLI()
|
||||||
tabs = b.tabs_list()
|
|
||||||
b.open("https://example.com")
|
tabs = b.tabs_list() # list[Tab]
|
||||||
|
tabs[0].close()
|
||||||
|
tabs[0].move(forward=True)
|
||||||
|
|
||||||
|
groups = b.group_list() # list[Group]
|
||||||
|
groups[0].tabs()
|
||||||
|
groups[0].add_tab("https://example.com")
|
||||||
|
|
||||||
|
# When multiple browser instances are active, pass the alias:
|
||||||
|
b = BrowserCLI(browser="brave")
|
||||||
"""
|
"""
|
||||||
from browser_cli.client import BrowserNotConnected, send_command
|
from browser_cli.client import BrowserNotConnected, send_command
|
||||||
|
from browser_cli.models import Group, Tab
|
||||||
|
|
||||||
__all__ = ["BrowserCLI", "BrowserNotConnected"]
|
__all__ = ["BrowserCLI", "BrowserNotConnected", "Tab", "Group"]
|
||||||
|
|
||||||
|
|
||||||
class BrowserCLI:
|
class BrowserCLI:
|
||||||
|
def __init__(self, browser: str | None = None):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
browser: Profile alias to target. Required when multiple browser
|
||||||
|
instances are active. Equivalent to ``--browser`` on the CLI.
|
||||||
|
"""
|
||||||
|
self._browser = browser
|
||||||
|
|
||||||
|
def _cmd(self, command: str, args: dict | None = None):
|
||||||
|
return send_command(command, args, profile=self._browser)
|
||||||
|
|
||||||
|
# ── Internal factories ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _make_tab(self, data: dict) -> Tab:
|
||||||
|
tab = Tab(
|
||||||
|
id=data["id"],
|
||||||
|
window_id=data.get("windowId", 0),
|
||||||
|
active=data.get("active", False),
|
||||||
|
title=data.get("title") or "",
|
||||||
|
url=data.get("url") or "",
|
||||||
|
group_id=data.get("groupId") or None,
|
||||||
|
)
|
||||||
|
tab._browser = self
|
||||||
|
return tab
|
||||||
|
|
||||||
|
def _make_group(self, data: dict) -> Group:
|
||||||
|
group = Group(
|
||||||
|
id=data["id"],
|
||||||
|
title=data.get("title") or "",
|
||||||
|
color=data.get("color") or "",
|
||||||
|
collapsed=data.get("collapsed", False),
|
||||||
|
tab_count=data.get("tabCount", 0),
|
||||||
|
)
|
||||||
|
group._browser = self
|
||||||
|
return group
|
||||||
|
|
||||||
# ── Navigation ────────────────────────────────────────────────────────
|
# ── Navigation ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def open(self, url: str, *, background: bool = False, window: str | None = None):
|
def open(self, url: str, *, background: bool = False, window: str | None = None, group: str | None = None) -> None:
|
||||||
return send_command("navigate.open", {"url": url, "background": background, "window": window})
|
self._cmd("navigate.open", {"url": url, "background": background, "window": window, "group": group})
|
||||||
|
|
||||||
def reload(self, tab_id: int | None = None):
|
def reload(self, tab_id: int | None = None) -> None:
|
||||||
return send_command("navigate.reload", {"tabId": tab_id})
|
self._cmd("navigate.reload", {"tabId": tab_id})
|
||||||
|
|
||||||
def hard_reload(self, tab_id: int | None = None):
|
def hard_reload(self, tab_id: int | None = None) -> None:
|
||||||
return send_command("navigate.hard_reload", {"tabId": tab_id})
|
self._cmd("navigate.hard_reload", {"tabId": tab_id})
|
||||||
|
|
||||||
def back(self, tab_id: int | None = None):
|
def back(self, tab_id: int | None = None) -> None:
|
||||||
return send_command("navigate.back", {"tabId": tab_id})
|
self._cmd("navigate.back", {"tabId": tab_id})
|
||||||
|
|
||||||
def forward(self, tab_id: int | None = None):
|
def forward(self, tab_id: int | None = None) -> None:
|
||||||
return send_command("navigate.forward", {"tabId": tab_id})
|
self._cmd("navigate.forward", {"tabId": tab_id})
|
||||||
|
|
||||||
def focus_url(self, pattern: str):
|
def focus_url(self, pattern: str) -> None:
|
||||||
return send_command("navigate.focus", {"pattern": pattern})
|
self._cmd("navigate.focus", {"pattern": pattern})
|
||||||
|
|
||||||
|
# ── Search ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def search(
|
||||||
|
self, engine: str, query: str, *,
|
||||||
|
background: bool = False, window: str | None = None, group: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Open a search query in the given engine (e.g. 'google', 'youtube', 'ddg')."""
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
from browser_cli.commands.search import ENGINES
|
||||||
|
template = ENGINES.get(engine)
|
||||||
|
if template is None:
|
||||||
|
raise ValueError(f"Unknown search engine '{engine}'. Available: {', '.join(ENGINES)}")
|
||||||
|
url = template.format(query=quote_plus(query))
|
||||||
|
self._cmd("navigate.open", {"url": url, "background": background, "window": window, "group": group})
|
||||||
|
|
||||||
# ── Tabs ──────────────────────────────────────────────────────────────
|
# ── Tabs ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def tabs_list(self) -> list[dict]:
|
def tabs_list(self) -> list[Tab]:
|
||||||
return send_command("tabs.list", {})
|
"""Return all open tabs across all windows."""
|
||||||
|
return [self._make_tab(t) for t in (self._cmd("tabs.list", {}) or [])]
|
||||||
|
|
||||||
def tabs_close(self, tab_id: int | None = None, *, inactive: bool = False, duplicates: bool = False):
|
def tabs_close(self, tab_id: int | None = None, *, inactive: bool = False, duplicates: bool = False) -> int:
|
||||||
return send_command("tabs.close", {"tabId": tab_id, "inactive": inactive, "duplicates": duplicates})
|
"""Close tab(s). Returns the number of tabs closed."""
|
||||||
|
result = self._cmd("tabs.close", {"tabId": tab_id, "inactive": inactive, "duplicates": duplicates})
|
||||||
|
return result.get("closed", 1) if isinstance(result, dict) else 1
|
||||||
|
|
||||||
def tabs_move(self, tab_id: int, *, group_id: int | None = None, window_id: int | None = None):
|
def tabs_move(
|
||||||
return send_command("tabs.move", {"tabId": tab_id, "groupId": group_id, "windowId": window_id})
|
self, tab_id: int, *,
|
||||||
|
forward: bool = False, backward: bool = False,
|
||||||
|
group_id: int | None = None, window_id: int | None = None, index: int | None = None,
|
||||||
|
) -> None:
|
||||||
|
self._cmd("tabs.move", {
|
||||||
|
"tabId": tab_id, "forward": forward, "backward": backward,
|
||||||
|
"groupId": group_id, "windowId": window_id, "index": index,
|
||||||
|
})
|
||||||
|
|
||||||
def tabs_active(self, tab_id: int):
|
def tabs_active(self, tab_id: int) -> None:
|
||||||
return send_command("tabs.active", {"tabId": tab_id})
|
"""Switch browser focus to a tab by ID."""
|
||||||
|
self._cmd("tabs.active", {"tabId": tab_id})
|
||||||
|
|
||||||
def tabs_filter(self, pattern: str) -> list[dict]:
|
def tabs_filter(self, pattern: str) -> list[Tab]:
|
||||||
return send_command("tabs.filter", {"pattern": pattern})
|
"""Return tabs whose URL contains *pattern*."""
|
||||||
|
return [self._make_tab(t) for t in (self._cmd("tabs.filter", {"pattern": pattern}) or [])]
|
||||||
|
|
||||||
def tabs_count(self, pattern: str | None = None) -> int:
|
def tabs_count(self, pattern: str | None = None) -> int:
|
||||||
return send_command("tabs.count", {"pattern": pattern})
|
"""Count open tabs, optionally filtered by URL pattern."""
|
||||||
|
return self._cmd("tabs.count", {"pattern": pattern})
|
||||||
|
|
||||||
def tabs_query(self, search: str) -> list[dict]:
|
def tabs_query(self, search: str) -> list[Tab]:
|
||||||
return send_command("tabs.query", {"search": search})
|
"""Search tabs by URL or title."""
|
||||||
|
return [self._make_tab(t) for t in (self._cmd("tabs.query", {"search": search}) or [])]
|
||||||
|
|
||||||
def tabs_html(self, tab_id: int | None = None) -> str:
|
def tabs_html(self, tab_id: int | None = None) -> str:
|
||||||
return send_command("tabs.html", {"tabId": tab_id})
|
"""Return the full HTML source of the active (or specified) tab."""
|
||||||
|
return self._cmd("tabs.html", {"tabId": tab_id}) or ""
|
||||||
|
|
||||||
def tabs_dedupe(self):
|
def tabs_dedupe(self) -> int:
|
||||||
return send_command("tabs.dedupe", {})
|
"""Close duplicate tabs (keep the first occurrence of each URL). Returns count closed."""
|
||||||
|
result = self._cmd("tabs.dedupe", {})
|
||||||
|
return result.get("closed", 0) if isinstance(result, dict) else 0
|
||||||
|
|
||||||
def tabs_sort(self, by: str = "domain"):
|
def tabs_sort(self, by: str = "domain") -> None:
|
||||||
return send_command("tabs.sort", {"by": by})
|
"""Sort tabs within each window. *by* is one of 'domain', 'title', 'time'."""
|
||||||
|
self._cmd("tabs.sort", {"by": by})
|
||||||
|
|
||||||
def tabs_merge_windows(self):
|
def tabs_merge_windows(self) -> int:
|
||||||
return send_command("tabs.merge_windows", {})
|
"""Move all tabs into the focused window. Returns count moved."""
|
||||||
|
result = self._cmd("tabs.merge_windows", {})
|
||||||
|
return result.get("moved", 0) if isinstance(result, dict) else 0
|
||||||
|
|
||||||
def tabs_close_inactive(self):
|
def tabs_close_inactive(self) -> int:
|
||||||
return send_command("tabs.close", {"inactive": True})
|
"""Close all inactive tabs. Returns count closed."""
|
||||||
|
result = self._cmd("tabs.close", {"inactive": True})
|
||||||
|
return result.get("closed", 0) if isinstance(result, dict) else 0
|
||||||
|
|
||||||
def tabs_close_duplicates(self):
|
def tabs_close_duplicates(self) -> int:
|
||||||
return send_command("tabs.close", {"duplicates": True})
|
"""Close duplicate tabs. Returns count closed."""
|
||||||
|
result = self._cmd("tabs.close", {"duplicates": True})
|
||||||
|
return result.get("closed", 0) if isinstance(result, dict) else 0
|
||||||
|
|
||||||
# ── Tab Groups ────────────────────────────────────────────────────────
|
# ── Tab Groups ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def group_list(self) -> list[dict]:
|
def group_list(self) -> list[Group]:
|
||||||
return send_command("group.list", {})
|
"""Return all tab groups."""
|
||||||
|
return [self._make_group(g) for g in (self._cmd("group.list", {}) or [])]
|
||||||
|
|
||||||
def group_tabs(self, group_id: int) -> list[dict]:
|
def group_tabs(self, group_id: int) -> list[Tab]:
|
||||||
return send_command("group.tabs", {"groupId": group_id})
|
"""Return all tabs inside a group."""
|
||||||
|
return [self._make_tab(t) for t in (self._cmd("group.tabs", {"groupId": group_id}) or [])]
|
||||||
|
|
||||||
def group_count(self) -> int:
|
def group_count(self) -> int:
|
||||||
return send_command("group.count", {})
|
"""Return the number of tab groups."""
|
||||||
|
return self._cmd("group.count", {})
|
||||||
|
|
||||||
def group_query(self, search: str) -> list[dict]:
|
def group_query(self, search: str) -> list[Group]:
|
||||||
return send_command("group.query", {"search": search})
|
"""Search groups by name."""
|
||||||
|
return [self._make_group(g) for g in (self._cmd("group.query", {"search": search}) or [])]
|
||||||
|
|
||||||
def group_close(self, group_id: int):
|
def group_close(self, group_id: int) -> None:
|
||||||
return send_command("group.close", {"groupId": group_id})
|
"""Ungroup (and close) a tab group by ID."""
|
||||||
|
self._cmd("group.close", {"groupId": group_id})
|
||||||
|
|
||||||
def group_open(self, name: str) -> dict:
|
def group_create(self, name: str) -> Group:
|
||||||
return send_command("group.open", {"name": name})
|
"""Create a new tab group with *name*. Returns the created Group."""
|
||||||
|
data = self._cmd("group.open", {"name": name})
|
||||||
|
return self._make_group(data) if isinstance(data, dict) else Group(id=data, title=name, color="", collapsed=False, tab_count=0)
|
||||||
|
|
||||||
|
def group_add_tab(self, group: str | int, url: str | None = None) -> int | None:
|
||||||
|
"""Open a new tab (optionally at URL) inside a group. Returns the new tab ID."""
|
||||||
|
result = self._cmd("group.add_tab", {"group": str(group), "url": url})
|
||||||
|
return result.get("tabId") if isinstance(result, dict) else result
|
||||||
|
|
||||||
|
def group_move(self, group: str | int, *, forward: bool = False, backward: bool = False) -> None:
|
||||||
|
"""Move a tab group forward or backward."""
|
||||||
|
self._cmd("group.move", {"group": str(group), "forward": forward, "backward": backward})
|
||||||
|
|
||||||
# ── Windows ───────────────────────────────────────────────────────────
|
# ── Windows ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def windows_list(self) -> list[dict]:
|
def windows_list(self) -> list[dict]:
|
||||||
return send_command("windows.list", {})
|
return self._cmd("windows.list", {})
|
||||||
|
|
||||||
def windows_rename(self, window_id: int, name: str):
|
def windows_rename(self, window_id: int, name: str) -> None:
|
||||||
return send_command("windows.rename", {"windowId": window_id, "name": name})
|
self._cmd("windows.rename", {"windowId": window_id, "name": name})
|
||||||
|
|
||||||
def windows_close(self, window_id: int):
|
def windows_close(self, window_id: int) -> None:
|
||||||
return send_command("windows.close", {"windowId": window_id})
|
self._cmd("windows.close", {"windowId": window_id})
|
||||||
|
|
||||||
def windows_open(self, profile: str | None = None) -> dict:
|
def windows_open(self, profile: str | None = None) -> dict:
|
||||||
return send_command("windows.open", {"profile": profile})
|
return self._cmd("windows.open", {"profile": profile})
|
||||||
|
|
||||||
# ── DOM ───────────────────────────────────────────────────────────────
|
# ── DOM ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def dom_query(self, selector: str) -> list[dict]:
|
def dom_query(self, selector: str) -> list[dict]:
|
||||||
return send_command("dom.query", {"selector": selector})
|
return self._cmd("dom.query", {"selector": selector})
|
||||||
|
|
||||||
def dom_click(self, selector: str):
|
def dom_click(self, selector: str) -> None:
|
||||||
return send_command("dom.click", {"selector": selector})
|
self._cmd("dom.click", {"selector": selector})
|
||||||
|
|
||||||
def dom_type(self, selector: str, text: str):
|
def dom_type(self, selector: str, text: str) -> None:
|
||||||
return send_command("dom.type", {"selector": selector, "text": text})
|
self._cmd("dom.type", {"selector": selector, "text": text})
|
||||||
|
|
||||||
def dom_attr(self, selector: str, attr: str) -> list[str]:
|
def dom_attr(self, selector: str, attr: str) -> list[str]:
|
||||||
return send_command("dom.attr", {"selector": selector, "attr": attr})
|
return self._cmd("dom.attr", {"selector": selector, "attr": attr})
|
||||||
|
|
||||||
def dom_text(self, selector: str) -> list[str]:
|
def dom_text(self, selector: str) -> list[str]:
|
||||||
return send_command("dom.text", {"selector": selector})
|
return self._cmd("dom.text", {"selector": selector})
|
||||||
|
|
||||||
def dom_exists(self, selector: str) -> bool:
|
def dom_exists(self, selector: str) -> bool:
|
||||||
return send_command("dom.exists", {"selector": selector})
|
return self._cmd("dom.exists", {"selector": selector})
|
||||||
|
|
||||||
# ── Extract ───────────────────────────────────────────────────────────
|
# ── Extract ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def extract_links(self) -> list[dict]:
|
def extract_links(self) -> list[dict]:
|
||||||
return send_command("extract.links", {})
|
return self._cmd("extract.links", {})
|
||||||
|
|
||||||
def extract_images(self) -> list[dict]:
|
def extract_images(self) -> list[dict]:
|
||||||
return send_command("extract.images", {})
|
return self._cmd("extract.images", {})
|
||||||
|
|
||||||
def extract_text(self) -> str:
|
def extract_text(self) -> str:
|
||||||
return send_command("extract.text", {})
|
return self._cmd("extract.text", {})
|
||||||
|
|
||||||
def extract_json(self, selector: str):
|
def extract_json(self, selector: str):
|
||||||
return send_command("extract.json", {"selector": selector})
|
return self._cmd("extract.json", {"selector": selector})
|
||||||
|
|
||||||
# ── Session ───────────────────────────────────────────────────────────
|
# ── Session ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def session_save(self, name: str):
|
def session_save(self, name: str) -> None:
|
||||||
return send_command("session.save", {"name": name})
|
self._cmd("session.save", {"name": name})
|
||||||
|
|
||||||
def session_load(self, name: str):
|
def session_load(self, name: str) -> None:
|
||||||
return send_command("session.load", {"name": name})
|
self._cmd("session.load", {"name": name})
|
||||||
|
|
||||||
def session_diff(self, name_a: str, name_b: str) -> dict:
|
def session_diff(self, name_a: str, name_b: str) -> dict:
|
||||||
return send_command("session.diff", {"nameA": name_a, "nameB": name_b})
|
return self._cmd("session.diff", {"nameA": name_a, "nameB": name_b})
|
||||||
|
|
||||||
def session_list(self) -> list[dict]:
|
def session_list(self) -> list[dict]:
|
||||||
return send_command("session.list", {})
|
return self._cmd("session.list", {})
|
||||||
|
|
||||||
def session_remove(self, name: str):
|
def session_remove(self, name: str) -> None:
|
||||||
return send_command("session.remove", {"name": name})
|
self._cmd("session.remove", {"name": name})
|
||||||
|
|
||||||
def session_auto_save(self, enabled: bool):
|
def session_auto_save(self, enabled: bool) -> None:
|
||||||
return send_command("session.auto_save", {"enabled": enabled})
|
self._cmd("session.auto_save", {"enabled": enabled})
|
||||||
|
|
||||||
# ── Misc ──────────────────────────────────────────────────────────────
|
# ── Misc ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def clients(self) -> list[dict]:
|
def clients(self) -> list[dict]:
|
||||||
return send_command("clients.list", {})
|
return self._cmd("clients.list", {})
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
Typed dataclasses returned by the BrowserCLI Python API.
|
||||||
|
|
||||||
|
Each object is bound to a BrowserCLI instance so you can call actions
|
||||||
|
directly on it:
|
||||||
|
|
||||||
|
tabs = b.tabs_list()
|
||||||
|
tabs[0].close()
|
||||||
|
tabs[0].move(forward=True)
|
||||||
|
|
||||||
|
groups = b.group_list()
|
||||||
|
groups[0].tabs()
|
||||||
|
groups[0].add_tab("https://example.com")
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from browser_cli import BrowserCLI
|
||||||
|
|
||||||
|
|
||||||
|
# ── Tab ───────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Tab:
|
||||||
|
"""A browser tab."""
|
||||||
|
id: int
|
||||||
|
window_id: int
|
||||||
|
active: bool
|
||||||
|
title: str
|
||||||
|
url: str
|
||||||
|
group_id: int | None = None
|
||||||
|
_browser: BrowserCLI | None = field(default=None, repr=False, compare=False, init=False)
|
||||||
|
|
||||||
|
def _b(self) -> BrowserCLI:
|
||||||
|
if self._browser is None:
|
||||||
|
raise RuntimeError("Tab is not bound to a BrowserCLI instance")
|
||||||
|
return self._browser
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""Close this tab."""
|
||||||
|
self._b()._cmd("tabs.close", {"tabId": self.id})
|
||||||
|
|
||||||
|
def activate(self) -> None:
|
||||||
|
"""Switch browser focus to this tab."""
|
||||||
|
self._b()._cmd("tabs.active", {"tabId": self.id})
|
||||||
|
|
||||||
|
def reload(self) -> None:
|
||||||
|
"""Reload this tab."""
|
||||||
|
self._b()._cmd("navigate.reload", {"tabId": self.id})
|
||||||
|
|
||||||
|
def hard_reload(self) -> None:
|
||||||
|
"""Hard-reload this tab (bypass cache)."""
|
||||||
|
self._b()._cmd("navigate.hard_reload", {"tabId": self.id})
|
||||||
|
|
||||||
|
def move(
|
||||||
|
self, *,
|
||||||
|
forward: bool = False,
|
||||||
|
backward: bool = False,
|
||||||
|
group_id: int | None = None,
|
||||||
|
window_id: int | None = None,
|
||||||
|
index: int | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Move this tab.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
forward: Move one position to the right within the window.
|
||||||
|
backward: Move one position to the left within the window.
|
||||||
|
group_id: Move into the tab group with this ID.
|
||||||
|
window_id: Move to the window with this ID.
|
||||||
|
index: Absolute position index in the target window.
|
||||||
|
"""
|
||||||
|
self._b()._cmd("tabs.move", {
|
||||||
|
"tabId": self.id,
|
||||||
|
"forward": forward,
|
||||||
|
"backward": backward,
|
||||||
|
"groupId": group_id,
|
||||||
|
"windowId": window_id,
|
||||||
|
"index": index,
|
||||||
|
})
|
||||||
|
|
||||||
|
def html(self) -> str:
|
||||||
|
"""Return the full HTML source of this tab."""
|
||||||
|
return self._b()._cmd("tabs.html", {"tabId": self.id})
|
||||||
|
|
||||||
|
def open(self, url: str, *, background: bool = False) -> None:
|
||||||
|
"""Navigate this tab to *url*."""
|
||||||
|
# Re-uses navigate.open which opens a new tab; for in-place navigation
|
||||||
|
# we target by tabId via the focus then navigate approach. For now we
|
||||||
|
# open a new tab in the same window as a convenience.
|
||||||
|
self._b()._cmd("navigate.open", {"url": url, "background": background})
|
||||||
|
|
||||||
|
|
||||||
|
# ── Group ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Group:
|
||||||
|
"""A browser tab group."""
|
||||||
|
id: int
|
||||||
|
title: str
|
||||||
|
color: str
|
||||||
|
collapsed: bool
|
||||||
|
tab_count: int
|
||||||
|
_browser: BrowserCLI | None = field(default=None, repr=False, compare=False, init=False)
|
||||||
|
|
||||||
|
def _b(self) -> BrowserCLI:
|
||||||
|
if self._browser is None:
|
||||||
|
raise RuntimeError("Group is not bound to a BrowserCLI instance")
|
||||||
|
return self._browser
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""Ungroup (and close) this tab group."""
|
||||||
|
self._b()._cmd("group.close", {"groupId": self.id})
|
||||||
|
|
||||||
|
def tabs(self) -> list[Tab]:
|
||||||
|
"""Return all tabs inside this group."""
|
||||||
|
return self._b().group_tabs(self.id)
|
||||||
|
|
||||||
|
def move(self, *, forward: bool = False, backward: bool = False) -> None:
|
||||||
|
"""Move this group forward or backward among groups."""
|
||||||
|
self._b()._cmd("group.move", {
|
||||||
|
"group": str(self.id),
|
||||||
|
"forward": forward,
|
||||||
|
"backward": backward,
|
||||||
|
})
|
||||||
|
|
||||||
|
def add_tab(self, url: str | None = None) -> int | None:
|
||||||
|
"""Open a new tab inside this group. Returns the new tab ID."""
|
||||||
|
return self._b().group_add_tab(self.id, url)
|
||||||
Reference in New Issue
Block a user