Личный проект

MonoStart: настраиваемое расширение Chrome для новой вкладки

Расширение Chrome с приоритетом приватности, которое превращает каждую новую вкладку в персональный дашборд со ссылками, таймерами и напоминаниями, которые хранятся полностью на вашем устройстве.

MonoStart превращает стандартную страницу новой вкладки браузера в быстрый, настраиваемый персональный дашборд. Он организует закладки, списки задач, быстрые заметки и таймеры фокусировки в настраиваемом рабочем пространстве. Построенное как расширение Chrome на Manifest V3, оно хранит всё локально на устройстве: без аккаунтов, без серверной базы данных, без аналитики.

MonoStart: настраиваемое расширение Chrome для новой вкладки

Что я сделал

  • Настраиваемый дашборд: сетка с перетаскиванием, включая вложенные группы виджетов, где виджеты (быстрые ссылки, папки по категориям, заметки, списки задач, таймеры обратного отсчёта, напоминания, голосовой поиск и визуальный поиск Google Lens) можно расположить под любой рабочий процесс.
  • Компоновка для быстрого доступа: закреплённые ссылки в шапке и сгруппированные папки заменяют глубокие меню закладок, оставляя часто используемые сайты в одном клике.
  • Настройка темы: 15 встроенных цветовых тем, восстановленных обратной разработкой из собственной палитры Chrome «Настроить Chrome», произвольный выбор цвета по hex-коду, поддержка светлого/тёмного режима, а также градиентные фоны или обои.
  • Инструменты продуктивности: встроенные таймеры обратного отсчёта и запланированные напоминания с локальными звуковыми оповещениями и уведомлениями рабочего стола.
  • Хранение с приоритетом локальности: заметки, задачи, расположение и изображения хранятся в chrome.storage.local и IndexedDB на устройстве. Нет аккаунтов пользователей и серверной базы данных; единственный сетевой вызов, это обращение к публичному API подсказок Google для автодополнения поиска, тому же, которым пользуется адресная строка самого Chrome.

Почему это было важно

Страница новой вкладки открывается десятки раз в день. MonoStart превращает это пустое пространство в мгновенный дашборд продуктивности с приоритетом локальности, который уважает вашу приватность: ваши данные никогда не покидают устройство.

Зачем это существует

Я открываю новую вкладку около 200 раз в день, и годами она не давала ничего в ответ: строку поиска и сетку сайтов, которые угадывал Google. Настоящей проблемой была не пустая страница, а всё, что я делал, чтобы с ней справляться. Больше сорока вкладок держались открытыми в трёх профилях Chrome только для того, чтобы не перепечатывать URL, каждая из них, живой процесс, который Chrome держал прогретым. Я не организовывал свою работу, я платил налог оперативной памятью и шумом вентилятора за то, чтобы держаться за указатели, а не за страницы. Каждое существующее расширение для новой вкладки, которое я пробовал, меняло эту пустую страницу на что-то похуже: чужеродный UI, ломавший мышечную память, аккаунт и облачную синхронизацию, спонсорские ссылки, телеметрию. Я единственный контрибьютор, 222 коммита с июня 2026 года, проект до сих пор активно поддерживается, построен сначала, чтобы починить мой собственный браузер, и лишь затем выпущен как публичное расширение.

Одно правило: выглядеть как Chrome, а не как приложение

Каждое решение в этой кодовой базе подчиняется одному ограничению: не должно быть заметно, где заканчивается Chrome и начинается дашборд. Поэтому в нём остаётся настоящая строка поиска Google с настоящими подсказками истории Chrome, вместо самодельного UI поиска, которому понадобился бы собственный индекс истории. Поэтому нет ни онбординга, ни экрана входа, ни системы аккаунтов: открытие новой вкладки должно ощущаться так, будто вообще ничего не устанавливали. Функция, из-за которой страница ощущалась бы отдельным приложением, какой бы полезной она ни была, не попадала в релиз.

Звук в service worker, который не может воспроизводить звук

Фоновые скрипты Manifest V3, это service worker'ы, а они не могут надёжно воспроизводить звук напрямую. Chrome приостанавливает и уничтожает их всякий раз, когда хочет освободить память, включая момент посреди воспроизведения. Напоминаниям всё равно нужен звук, который реально сработает. Решение использует chrome.alarms для планирования напоминания, поскольку будильники переживают убитый service worker и пробуждают его обратно, и направляет воспроизведение через документ chrome.offscreen вместо самого service worker'а, скрытую страницу, которую Chrome специально держит живой для таких задач. Это тот же класс обходного пути, с которым рано или поздно сталкивается большинство MV3-расширений, поскольку постоянной фоновой страницы в MV3 больше не существует.

Темы, восстановленные обратной разработкой из самого Chrome

Вместо того чтобы изобретать произвольную цветовую палитру, 15 встроенных тем MonoStart извлечены из исходных цветов собственной функции Chrome «Настроить Chrome», а процесс извлечения задокументирован по шагам в репозитории. В ранней версии этого извлечения была ошибка сдвига оттенка. Исправление вышло в v1.1.1 вместе с путём миграции для тех, кто уже выбрал тему по старой математике, так что существующие пользователи не проснулись с другим цветом, чем тот, что они выбрали.

Безопасность: санитизация встраиваемого контента без слепого доверия одному санитайзеру

Виджеты изображений и встраиваемого контента принимают произвольный HTML, который перед рендерингом санитизируется через DOMPurify. Одного DOMPurify было недостаточно: он не удаляет атрибут srcdoc, а iframe с установленным srcdoc наследует собственный origin расширения, реальный путь для XSS, если CSP расширения когда-нибудь ослабят. srcdoc явно вырезается в санитайзере независимо от того, что сам по себе разрешает DOMPurify, это второй, независимый слой вместо полного доверия одной библиотеке.

Модель данных и масштаб

  • Текстовые данные (заметки, задачи, расположение, напоминания, настройки) хранятся в chrome.storage.local.
  • Изображения обоев и фонов хранятся отдельно, в базе данных IndexedDB, со сборкой мусора по «осиротевшим» ссылкам, так что удалённые или заменённые изображения не накапливаются молча.
  • Лимит примерно в 10 МБ у chrome.storage.local снят через разрешение unlimitedStorage, необходимое после того, как изображения полностью переехали с этой квоты.
  • 216 тестов в 25 файлах (Vitest, jsdom, fake-indexeddb) напрямую покрывают этот слой хранения, а не только UI поверх него.

Хранение только локально, это осознанный компромисс, а не недосмотр: это значит, что нет синхронизации между машинами или профилями, но это тот же механизм, который бесплатно даёт трём профилям Chrome собственный независимый, изолированный дашборд, без общей системы аккаунтов, которую нужно строить и защищать. Автодополнение в строке поиска, единственный сетевой вызов во всём приложении, обращающийся к публичному API подсказок Google так же, как это делает адресная строка самого Chrome; всё, что пользователь реально создаёт, остаётся локальным.

Своё решение или готовое

Никакого самодельного движка drag-and-drop: react-grid-layout берёт на себя сетку, включая вложенные группы, вместо того чтобы заново реализовывать обнаружение столкновений и маркеры изменения размера с нуля. Никакой самодельной библиотеки компонентов: подход shadcn/ui, копировать компоненты на основе Radix прямо в проект (вместо установки непрозрачного UI-пакета), оставляет код каждого компонента и любое отклонение от него видимым и редактируемым. Никакого самодельного конвейера локализации: react-i18next плюс растущий набор файлов локалей (50+ языков) вместо самописного поиска строк.

Стек

React 19, Vite, TypeScript, Tailwind CSS, shadcn/ui на примитивах Radix UI, react-grid-layout для сетки дашборда, react-i18next для локализации, DOMPurify для санитизации встраиваемого контента, Vitest для тестов, платформа расширений Chrome Manifest V3 (chrome.alarms, chrome.offscreen, chrome.storage, IndexedDB), лицензировано под PolyForm Noncommercial 1.0.0.