- одно расширение
- Без сервера, базы и бэкенда, всё работает в браузере. Две части: окно расширения и скрипт со стилями на странице.
- вслепую
- Доступа к целевому сайту не было, вёрстка известна только по скриншоту. Для разработки — демо-копия панели вкладок и маленький сервер на Node.js.
- автопоиск вкладок
- Группа из 4 и больше коротких однотипных кнопок или ссылок у общего родителя; role="tab" и классы со словом tab — в приоритете. Промах — селектор задаётся в окне расширения и подхватывается сразу через chrome.storage.sync.
- перерисовка сайта
- SPA вроде React затирают классы при переключении вкладок. MutationObserver с задержкой 200 мс возвращает покраску, а свои изменения и свои элементы пропускает, поэтому не уходит в бесконечный цикл.
- доступ к одному сайту
- Адреса сайта в коде нет: доступ объявлен необязательным и запрашивается по клику в окне расширения, скрипт регистрируется динамически через chrome.scripting. Запрос — сразу по клику, до любых асинхронных шагов, иначе Chrome его отклоняет.
- перелёт в колесо
- Каждая вкладка летит из своего места на странице в своё место на круге. Набор функций трансформации одинаковый, поэтому браузер плавно ведёт каждую часть; на обратном пути угол берётся ближайший кратный 360°, без лишнего оборота.
- шарик точно на вкладке
- Результат выбирается заранее через crypto.getRandomValues. Конечный угол шарика = угол колеса + угол выпавшей вкладки: кривые замедления разные, но приходят они одновременно. Со второй половины спина шарик падает с дорожки на вкладку с отскоком.