brave-rofi-rust: переключение вкладок браузера из любого окна

Меню на Rust + rofi, которое переключает вкладки, закладки и историю браузера из любого окна в i3 — а не только когда браузер в фокусе. Оно управляет Brave по Chrome DevTools Protocol, а поднятие окна отдаёт хуку, так что один хоткей работает везде.

Меню rofi со списком вкладок браузера Brave для переключения

На macOS есть воркфлоу Alfred для переключения вкладок браузера: жмёшь хоткей откуда угодно, фаззи-поиском находишь нужную вкладку, прыгаешь прямо в неё. В i3 с rofi ничего подобного у меня не было. Единственный специально сделанный инструмент, который я нашёл — brotab, управляющий вкладками из CLI через расширение браузера, — у меня так и не заработал, по причинам, которые я уже забыл. А сами по себе расширения браузера и скрипты на Puppeteer страдают другой проблемой: они помогают в основном только когда браузер уже в фокусе. А весь смысл в том, чтобы попасть в него, когда он не в фокусе.

Так появился brave-rofi-rust — небольшая программа на Rust, которая складывает открытые вкладки (плюс закладки и историю) в меню rofi, вызываемое из любого окна. Выбираешь вкладку из редактора, терминала, откуда угодно — и браузер выходит на передний план на нужной странице.

Стек
Rust, tokio, Chrome DevTools Protocol, rofi, rusqlite
Задача
Глобальный переключатель вкладок / закладок / истории на i3 + rofi
Статус
Черновик / в работе
Лицензия
MIT OR Apache-2.0

Что он делает

Схема: хоткей rofi из любого окна запускает brave-rofi, который получает список вкладок по Chrome DevTools Protocol на localhost:9222, открывает закладки из файла surfraw или запись истории из sqlite браузера, а затем хук после переключения (например, i3-msg) поднимает окно браузера.
Один хоткей, любое активное окно. CDP переключает вкладку внутри браузера; хук после переключения поднимает окно браузера.

Привяжи brave-rofi к клавише в i3 — и получишь глобальный переключатель:

  • Вкладки — все открытые вкладки в фаззи-меню; выбираешь — активируется.
  • Закладки — открыть закладку откуда угодно, обычную или в инкогнито.
  • История — открыть запись истории откуда угодно.
  • Плюс поиск (обычный/инкогнито), новая вкладка и закрытие с мультивыбором.

Поддерживается семейство Chromium — Brave Beta (по умолчанию), Brave и Chromium — выбирается переменной окружения $BROWSER.

Почему нужны два протокола

Самое интересное: «переключиться на вкладку» — это на самом деле две разные задачи, и ни один инструмент не делает обе.

Chrome DevTools Protocol (CDP) умеет переключать активную вкладку внутри браузера. brave-rofi запрашивает у браузера список вкладок по http://localhost:9222/json (браузер нужно запустить с --remote-debugging-port=9222), показывает их в rofi, а по выбору шлёт Target.activateTarget через WebSocket. Это фокусирует нужную вкладку — но CDP понятия не имеет о твоём оконном менеджере, так что если окно браузера завалено под редактором, визуально ничего не происходит.

Поднять окно ОС — задача i3, а не CDP. Поэтому после переключения вкладки brave-rofi выполняет то, что ты положишь в $BROWSER_POST_SWITCH_HOOK:

Terminal window
export BROWSER_POST_SWITCH_HOOK='i3-msg [class="Brave-browser"] focus'

Разделение этих двух обязанностей — CDP для вкладки, пользовательский хук для окна — и есть то, что заставляет инструмент работать из любого активного приложения и не привязывает его к i3. Замени хук на swaymsg или wmctrl — и он заработает на sway или в чистом X11.

любое активное окно ──хоткей──▶ brave-rofi ──curl──▶ /json (список вкладок, CDP)
меню rofi ──┤── вкладка → CDP Target.activateTarget
├── закладка → запуск $BROWSER <url>
└── история → запуск $BROWSER <url>
$BROWSER_POST_SWITCH_HOOK → поднять окно браузера

Закладки и история — тем же способом

Как только переключение вкладок заработало отовсюду, стало обидно, что закладки и история — нет. Так что я провёл их через тот же путь «rofi из любого окна».

История — прямой потомок fzf-сниппета, который я писал раньше: Просмотр истории браузера Brave. Тот же приём: браузер держит History заблокированной SQLite-базой, пока запущен, поэтому сначала копируешь её в /tmp, а затем запрашиваешь urls с сортировкой по last_visit_time. В той статье результат уходил в fzf в терминале; здесь тот же самый запрос кормит полноэкранное меню rofi, которое можно открыть, вообще не трогая браузер.

Закладки читаются из моего файла закладок surfraw (~/.config/surfraw/bookmarks) — простой текстовый файл имя url, который я и так веду, так что добавить закладку — это дописать одну строку. Выбираешь в rofi — открывается в браузере (или в окне инкогнито), а тот же хук поднимает окно.

В каком это состоянии

Честно: это черновик. Он закрывает мою личную боль, и по репозиторию это видно.

  • Только семейство Chromium. Zen и другие браузеры на базе Firefox говорят на другом протоколе удалённого управления и хранят историю в другой схеме (moz_places в places.sqlite). Переключение вкладок для Zen я начал и не доделал — это одна из нескольких недостроенных идей внутри.
  • Мёртвый код. Остались обрывки и наполовину смигрированные пути от прежних подходов (логика фокуса окна раньше была жёстко зашита под i3, до появления общего хука), которые я не вычистил.
  • Название может измениться. brave-rofi-rust описывает, откуда всё началось, а не где оно сейчас — он уже работает с Chromium и не так уж привязан к Brave. Если вырастет во что-то более цельное и функциональное, скорее всего переименую.
  • CDP, возможно, неправильная основа. Требование запускать браузер с --remote-debugging-port=9222 — реальная заноза. Расширение браузера, общающееся через native messaging — примерно как устроен brotab, — вероятно, было бы более чистой основой, чем управление по CDP снаружи, даже ценой отказа от простоты одного статического бинарника. В сторону этого я бы и копал при переосмыслении.

Воспринимай это как рабочий прототип идеи, которая мне нравится, а не как законченный инструмент.

Попробовать

Terminal window
cargo install --git https://github.com/antlis/brave-rofi-rust.git

Запусти браузер с --remote-debugging-port=9222, задай $BROWSER и $BROWSER_POST_SWITCH_HOOK, затем привяжи в i3:

bindsym $mod+b exec --no-startup-id brave-rofi

В README — полная настройка и актуальный список шероховатостей. Issues и PR приветствуются — особенно если заведёшь переключение для Firefox/Zen, потому что у меня не вышло.