Tabula — расширение для браузера, которое превращает новую вкладку в электронную таблицу. Закладки раскладываются по ячейкам сетки, как в Excel: со своими листами, темами, фоном, часами и погодой.
- Работает в Chromium-браузерах (Chrome, Edge, Brave, Opera, Vivaldi…) и Firefox 140+.
- Интерфейс — на русском, в настройках переключается на английский.
- Без подписок, без телеметрии — все данные хранятся локально в вашем браузере.
- Автор: withersky.
🌐 Сайт-визитка с кнопками скачивания: https://tabula.withersky.workers.dev/
- Скачайте архив Chrome со страницы релизов.
- Распакуйте его в любую папку.
- Откройте
chrome://extensions(илиedge://extensions) и включите «Режим разработчика». - Нажмите «Загрузить распакованное расширение» и выберите распакованную папку.
- Откройте новую вкладку — появится таблица. Настройки — иконка ⚙ в правом верхнем углу.
- Скачайте архив Firefox со страницы релизов.
- Распакуйте его в любую папку.
- Откройте
about:debugging#/runtime/this-firefox. - Нажмите «Загрузить временное дополнение…» и выберите файл
manifest.jsonиз папки.
Если другое расширение уже переопределяет новую вкладку, отключите его — браузер использует только одно такое расширение одновременно.
Яндекс Браузер не позволяет переопределять страницу новой вкладки
через chrome_url_overrides, поэтому схема запуска другая — расширение
«живёт» в панели расширений и открывает таблицу по клику на иконку:
- Скачайте архив Yandex со страницы релизов.
- Распакуйте его в любую папку.
- Откройте
browser://extensionsи включите «Режим разработчика» (внизу страницы). - Нажмите «Загрузить расширение» и выберите распакованную папку.
- Закрепите иконку Tabula в панели расширений (через меню «Показать на панели»), чтобы она всегда была под рукой.
- Клик по иконке открывает
newtab/newtab.htmlв новой вкладке. Настройки — иконка ⚙ в правом верхнем углу таблицы (или пункт в меню расширения).
Открытие по клику обеспечивает
src/background.js: вmanifest.yandex.jsonнетchrome_url_overrides, поэтому событиеaction.onClickedсрабатывает и создаёт вкладку с таблицей.
Листы и сетка
- Несколько листов внизу, как вкладки Excel: emoji-иконки, переименование двойным кликом, перетаскивание для смены порядка и контекстное меню. Число колонок (3–12) задаётся общим для всех листов в настройках (
defaultColumns). - Сетка ячеек: каждая ячейка — одна закладка. Пустые ячейки просто пустые, «привязки к строке» нет.
- Перетаскивание: на пустую ячейку — переместить, на заполненную — поменять местами.
- Клик — открыть закладку, Ctrl/Cmd+клик — в новой вкладке, средняя кнопка мыши — тоже новая вкладка.
- Правый клик — контекстное меню: открыть / в новой вкладке / изменить / дублировать / удалить.
Оформление
- Тёмная и светлая тема, линии сетки (вкл/выкл), номера строк и буквы колонок.
- Фон: сплошной цвет, CSS-градиент, URL картинки, своё изображение или Bing: изображение дня (обновляется автоматически).
- Шрифт вкладки, размер текста в ячейках, цвет текста.
Виджеты и удобства
- Строка быстрого поиска с подсказками и выбором поисковика (Google / Яндекс / Bing).
- Поиск по всем листам (палитра поиска,
Ctrl+F): ищет по названию и URL закладок на всех листах и переходит к найденной ячейке. - Часы и погода (несколько городов, период обновления; клик открывает попап с прогнозом по дням и по часам — активный город в шапке попапа раскрывает выпадающий список для быстрого переключения, дни в ленте часов раскрашены в шахматном порядке, а сама лента скроллится перетаскиванием мышью, колесом или тачем; формат дат настраивается или отключается). Размер виджетов управляется через общий масштаб.
- Свой заголовок вкладки, фавиконы закладок, импорт/экспорт всех данных в JSON.
Хоткеи
Работают и на русской, и на английской раскладках — буквы определяются по физическим клавишам, поэтому сочетания не зависят от текущего языка ввода. Справка по всем клавишам — F1 или ?.
| Клавиша | Действие |
|---|---|
↑ ↓ ← → |
Перемещение выделения по ячейкам |
Home / End |
Начало / конец строки |
Ctrl+Home / Ctrl+End |
Первая ячейка (A1) / последняя заполненная ячейка |
Enter / Space |
Открыть закладку |
Ctrl+Enter / Shift+Enter |
Открыть в новой вкладке |
F2 |
Редактировать закладку |
Insert |
Добавить закладку в выбранную ячейку (или первую свободную) |
Delete / Backspace |
Удалить закладку |
Ctrl+D |
Дублировать закладку |
Shift+F10 |
Контекстное меню выбранной ячейки |
PageUp / PageDown |
Предыдущий / следующий лист |
Ctrl+F |
Поиск по всем листам (палитра поиска) |
Ctrl+K / Ctrl+E |
Фокус в строку быстрого поиска |
/ |
Фокус в строку быстрого поиска |
F1 / ? |
Справка по горячим клавишам |
Esc |
Закрыть модалку / меню / палитру поиска / справку |
Настройки открываются иконкой ⚙ на новой вкладке. Вверху — единый sticky-блок из шапки и вкладок:
- Оформление — тема и линии сетки; колонки и строки новых листов; текст (шрифт вкладки, размер в ячейках, цвет); фон (сплошной цвет, градиент, URL картинки, своё изображение, Bing-обои дня); единая прозрачность панелей, виджетов и ячеек (с блюром) и цвет выделения (свой или AutoColor — автоматически подбирается под текущий фон); фавиконы, открытие в новой вкладке, номера строк и буквы колонок, заголовок вкладки.
- Виджеты — часы (города), строка быстрого поиска (поисковик, подсказки), погода (города, период обновления, число дней прогноза, формат дат), общий масштаб виджетов.
- Язык — Русский / English (переключается «на лету»).
- Данные — импорт/экспорт всего датасета в JSON.
- О программе — версия, ссылки на сайт и репозиторий.
Справа — живое превью новой вкладки. Оно показывается на вкладках «Оформление» и «Виджеты» и обновляется при каждом изменении настройки; ввод в модальных окнах (например, поиск города) превью не обновляет.
tabula-plugin/
├── src/ исходники расширения (содержимое = пакет)
│ ├── manifest.json MV3 (Chrome): service_worker, newtab override
│ ├── manifest.firefox.json MV3 (Firefox 140+): background.scripts, gecko.id
│ ├── manifest.yandex.json MV3 (Yandex): service_worker, БЕЗ newtab override
│ │ (запуск кликом по иконке из панели расширений)
│ ├── background.js service worker / event page: проксирует Bing и met.no
│ │ (ответы met.no кешируются в памяти: TTL 10 мин, до 8 городов)
│ ├── lib/
│ │ ├── browser.js кросс-браузерная обёртка ext.* (chrome.* / browser.*)
│ │ ├── core.js чистая логика: URL, погода (met.no), даты, ключи ячеек
│ │ ├── timezone.js ЕДИНЫЙ модуль таймзон (partsInTz / resolveTimezoneByName /
│ │ │ ensureCityTimezone) — общий для виджетов часов и погоды
│ │ └── storage.js дефолты, миграция v1→v2→v3, i18n, helpers
│ │ └── i18n-shared.js общая обвязка data-i18n* (глобал, используется newtab/options)
│ ├── i18n/ переводы (JSON — правят сообществом)
│ │ ├── ru.json, en.json словарь интерфейса
│ │ ├── symbols.{ru,en}.json описания погодных символов met.no
│ │ └── generated/ генерируется gen-i18n.mjs; в git не хранится
│ ├── newtab/ страница новой вкладки
│ │ ├── newtab.html
│ │ ├── newtab.css
│ │ └── js/ ES-модули (main, state, grid, sheets, weather, …)
│ ├── options/ страница настроек
│ │ ├── options.html
│ │ ├── options.css
│ │ └── js/ ES-модули (main, form, preview, widgets, data, …)
│ └── icons/
├── scripts/
│ └── gen-i18n.mjs генератор src/i18n/generated/*.js из JSON
├── build.sh сборка dist/{chrome,firefox,yandex} из src/
├── site/ сайт-визитка (Cloudflare Workers, заливка вручную)
│ └── icons/ копии иконок расширения (на Workers заливается папка site/)
├── tests/ юнит-тесты чистой логики (см. tests/README.md)
└── .github/workflows/
└── release.yml сборка релизов по коммитам `vX.Y.Z` (с прогоном тестов)
Сборка не нужна для правки JS/CSS/HTML: отредактируйте файлы в src/ и обновите
расширение из chrome://extensions (в Firefox — перезагрузите дополнение в about:debugging).
Все обращения к API расширения идут через тонкую обёртку src/lib/browser.js,
поэтому Chrome и Firefox используют одну и ту же кодовую базу.
Исключение — файлы манифеста. Правки src/manifest.json или
src/manifest.firefox.json влияют только на собранную
папку dist/. При загрузке unpacked из src/ браузер читает именно манифест из src/,
поэтому для dev-режима правки видны сразу; а для установленной/упакованной сборки —
после ./build.sh firefox (или all) и повторной загрузки dist/firefox.
Например, добавление lib/timezone.js в background.scripts манифеста Firefox без
пересборки оставит расширение без функции геокодинга таймзоны (resolveTimezoneByName
будет не определён в фоне). Синхронность dist/ с src/ проверяется скриптом
./scripts/check-sync.sh.
Перед первой загрузкой src/ как unpacked сгенерируйте браузерные i18n-скрипты —
в репозитории их нет (это производный артефакт):
node scripts/gen-i18n.mjs # требуется Node.js; повторяйте после правки src/i18n/*.json./build.sh chrome # -> dist/chrome/ (используется src/manifest.json)
./build.sh firefox # -> dist/firefox/ (используется src/manifest.firefox.json)
./build.sh all # -> оба вариантаПеред копированием build.sh генерирует браузерные i18n-скрипты из JSON-словарей
(node scripts/gen-i18n.mjs), так что после добавления нового языка достаточно пересобрать.
Полученные папки можно упаковывать в zip и загружать в магазины
(Chrome Web Store, AMO). Папки site/, dist/ и сам build.sh в архивы не попадают.
Проверить, что dist/ не разошёлся с src/ (после правок легко забыть
пересобрать), можно отдельным скриптом — он пересобирает и сверяет содержимое:
./scripts/check-sync.sh # exit 0 — dist синхронен с src; иначе список расхожденийЮнит-тесты чистой логики (src/lib/core.js, src/lib/storage.js,
src/lib/timezone.js) на Robot Framework — подробности в
tests/README.md.
Тесты покрывают и браузерозависимое поведение детерминированно (маркеры $gecko/$noLeadingZeroHour
воспроизводят особенности Firefox, например баг прогноза погоды для восточных поясов и
рендеринга дат без ведущего нуля; регресс-тест манифеста проверяет, что lib/timezone.js
подключён к фону в Firefox).
Расширение пишет в консоль браузера (DevTools → Console) только сообщения об ошибках и провалах сети — в счастливом пути логов нет, поэтому они не влияют на производительность. Ключевые места с логированием:
- фоновый скрипт
src/background.js— провалimportScripts(например, еслиlib/timezone.jsне подключён в манифесте Firefox) и ошибки сетевых обработчиков (Bing-картинка, подсказки поиска, met.no: геокодинг, реверс-геокодинг, прогноз); src/newtab/js/clock.js— провалpartsInTz(падение часов с некорректной IANA-таймзоной, с откатом на локальное время);src/newtab/js/weather.js— провал загрузки прогноза иpartsInTzв попапе.
./tests/run_tests.sh # прогнать все тесты
./tests/run_tests.sh --dryrun # только проверить синтаксис, без исполненияВоркфлоу .github/workflows/release.yml запускается
при каждом push в main/master. Если первая строка commit message начинается
с vX.Y.Z, он:
- Проверяет, что поле
"version"вsrc/manifest.jsonиsrc/manifest.firefox.jsonсовпадает сX.Y.Z(страховка от рассинхрона). - Собирает оба варианта через
./build.sh allи упаковывает вtabula-chrome-vX.Y.Z.zipиtabula-firefox-vX.Y.Z.zip. - Ставит git-тег
vX.Y.Zи публикует GitHub Release с архивами и полным текстом commit message в качестве release notes.
Обычные коммиты (без префикса vX.Y.Z) workflow тихо пропускает.
Процедура релиза:
- Поднимите версию в обоих манифестах (
src/manifest.json,src/manifest.firefox.json). - Коммит, первая строка которого выглядит так:
v1.2.3 - короткое описание релиза
- подробности изменения 1
- подробности изменения 2
git push origin main. Через ~30 секунд в разделе Releases появятся релиз с заголовкомTabula v1.2.3(git-тег при этом — простоv1.2.3) и два архива —tabula-chrome-v1.2.3-unsign.zip(Chrome) иtabula-firefox-v1.2.3-unsign.xpi(Firefox, неподписанный). Workflow также запишетsite/latest.jsonиsite/updates.jsonи закоммитит их. Файлы сайта попадут на сайт только после ручной заливки папкиsite/в Workers (см. раздел «Сайт-визитка»), поэтому кнопки скачивания будут указывать на новый релиз.
Автообновление Firefox (update_url):
Firefox-манифест src/manifest.firefox.json содержит
browser_specific_settings.gecko.update_url, указывающий на
site/updates.json (GitHub Pages). Это update-манифест,
по которому Firefox находит новые версии для пользователей, установивших
дополнение из релиза:
{
"addons": {
"tabula@withersky.local": {
"updates": [
{ "version": "1.2.3", "update_link": "https://github.com/withersky/tabula-plugin/releases/download/v1.2.3/tabula-firefox-v1.2.3.xpi" }
]
}
}
}Важное ограничение: Firefox по update_link устанавливает только подписанный
Mozilla .xpi (неподписанный -unsign файл он отклонит). Поэтому после того,
как заявка пройдёт ревью в Центре разработчиков:
- Скачайте подписанный
.xpiиз Центра разработчиков. - Переименуйте его в
tabula-firefox-v1.2.3.xpi(без суффикса-unsign). - Загрузите в тот же релиз
v1.2.3на GitHub (через интерфейс Releases → Attach a file или командойgh release upload).release.ymlсам дописываетupdate_linkна этот подписанный.xpiвsite/updates.json(безupdate_hash— self-hostedupdate_urlпринимает.xpiи без sha256, как подтверждено на практике). Дополнительных ручных шагов не требуется; Firefox увидит новую версию автоматически.
Ручной перезапуск сборки (без нового коммита): воркфлоу
.github/workflows/release.yml поддерживает
workflow_dispatch с опциональным полем version. Зайдите в Actions → release →
Run workflow, укажите ветку и, при необходимости, версию X.Y.Z (например
2.8.3), чтобы пересобрать/переопубликовать уже существующий релиз актуальным
флоу. Если поле version оставить пустым — версия определится из сообщения
последнего коммита (как при обычном push). Важно: кнопка Re-run провалившегося
запуска использует старую версию файла воркфлоу, поэтому для применения
правок флоу используйте именно workflow_dispatch или новый push.
Папка site/ — статичный одностраничный сайт с кнопками
скачивания. Стиль сайта повторяет интерфейс расширения (та же палитра, что в
src/options/options.css, и CSS-мокап новой вкладки), а в шапке и фавиконке
используется настоящая иконка Tabula, в кнопках скачивания — логотипы браузеров
(chromium.svg, firefox.svg). Копии всех этих ресурсов лежат в site/icons/
(на Workers заливается папка site/, поэтому иконки продублированы
рядом). Если вы поменяете src/icons/icon*.png или src/icons/*.svg — обновите
и копии в site/icons/.
Скрипт site/main.js подставляет кнопкам прямые ссылки на
файлы релиза. Сначала он читает статический снимок site/latest.json
(тот же origin — работает даже при недоступности api.github.com), затем для
точности фоново уточняет данные через GitHub API. Снимок обновляет release workflow
при каждом релизе, поэтому кнопки всегда ведут на актуальные
tabula-chrome-vX.Y.Z-unsign.zip и tabula-firefox-vX.Y.Z-unsign.xpi.
В той же папке лежит site/updates.json — Firefox
update-манифест для update_url из src/manifest.firefox.json
(сейчас указывает на https://tabula.withersky.workers.dev/updates.json).
Release workflow пишет его при каждом релизе: update_link ведёт на подписанный
.xpi из релиза, self-hosted update_url обновляет Firefox без update_hash.
Файл всегда актуален и доступен по https://tabula.withersky.workers.dev/updates.json.
Сайт деплоится автоматически при каждом пуше в подключённую ветку /site — через Workers Builds (нативная Git-интеграция Cloudflare, как у Pages, но для обычного Worker).
Конфигурация лежит в wrangler.toml:
name = "tabula-plugin", assets.directory = "site" — то есть воркер
отдаёт статическую папку site/ как есть (сборка не
нужна, build-шаг пустой).
Подключение репозитория (делается один раз в дашборде Cloudflare): см.
пошаговые пути в CLOUDFLARE-SETUP.md. Кратко:
Worker tabula-plugin → Settings → Builds → Connect Git repository,
ветка main, Build command пусто, Deploy command wrangler deploy, и
рекомендуется поставить Path filter site/**.
❄️ Старый воркфлоу GitHub Pages (
.github/workflows/site.yml) заморожен — из него убран push-триггер и добавлен guardinputs.deploy(по умолчаниюfalse), поэтому он ничего не публикует. Файл оставлен намеренно: если захотите вернуться на GitHub Pages, уберите guard и включите push-триггер обратно.
Релизы: release.yml обновляет site/latest.json
и site/updates.json и пушит их в main — а значит Cloudflare сам
пересоберёт и перезальёт сайт, включая актуальный Firefox update_url.
Дополнительных ручных действий после релиза не требуется; Firefox увидит
новую версию по https://tabula.withersky.workers.dev/updates.json автоматически.
Данные лежат в chrome.storage.local под ключом tabula_data, текущий формат — v3:
Storage.get() автоматически поднимает старые форматы:
v1 (tabs + groups) → каждая группа становится листом;
v2 (sheets[].tabs) → каждый tab превращается в cells["row,0"];
v3 (текущий) — без миграции. После миграции данные сохраняются обратно.
Частые правки:
- Стартовые закладки и листы —
defaultData()вsrc/lib/storage.js. - Дефолтная тема — объект
settingsтам же. - CSS-переменные (
--columns,--font-family, …) — в:rootвsrc/newtab/newtab.css. Строки грида делят доступную высоту поровну; их количество задаётсяsettings.gridRows. - Переводы — добавьте
src/i18n/<lang>.json(и при необходимостиsrc/i18n/symbols.<lang>.json), затем запуститеnode scripts/gen-i18n.mjsи добавьте radio-карточкуname="language"вsrc/options/options.html. JSON не требует правки JS-кода, поэтому переводы легко делать сообществу. Строки с плейсхолдерами используют{name}/{n}. - Ссылки на скачивание на сайте — меняются автоматически из последнего релиза, ничего править не нужно.
- Фавиконы подгружаются напрямую с сайта (
/favicon.ico, без внешних сервисов — ради приватности), кэшируются как data URL вchrome.storage.local(лимит ~8 МБ, до 400 хостов, LRU по времени). Если иконки нет — буквенный бейдж первой буквы названия. - Свои изображения хранятся как data URL в
chrome.storage.local(лимит ~2 МБ). - Bing daily подгружается через service worker (нужны
host_permissions), кешируется на день. - Погода met.no запрашивается через service worker; успешные ответы кешируются в памяти на 10 минут (до 8 городов), чтобы не дёргать API при частом открытии попапа.
- Часовые пояса городов (для виджетов часов и погоды) считаются в едином модуле
src/lib/timezone.js. У стран (feature_codePCLIв open-meteo) полеtimezoneотсутствует — поэтому страны нельзя добавить как город погоды/часов (иначе время считалось бы в UTC); при поиске они отфильтровываются. - Синхронизация между устройствами не предусмотрена — используйте экспорт/импорт JSON.
- Автообновление Firefox работает только через
update_urlна подписанный.xpi(см. раздел «Релизы»); неподписанные сборки пользователям не обновляются автоматически. - Требования: Manifest V3, Chrome 108+; Firefox 140+ (в Firefox-сборке используется
background.scripts).
{ "sheets": [ { "id": "uuid", "name": "Главная", "icon": "📋", // emoji для вкладки листа "columns": 8, // 3..12, сохраняется в данных, но рендер сетки // использует глобальную настройку defaultColumns "cells": { "0,0": { "id": "uuid", "title": "Google", "url": "https://google.com" }, "2,4": { "id": "uuid", "title": "GitHub", "url": "https://github.com" } // пустые ячейки просто отсутствуют в объекте } } ], "activeSheetId": "uuid", "settings": { "defaultColumns": 8, "fontFamilyKey": "system", "backgroundType": "gradient", // color | gradient | imageUrl | imageUpload | bing "showFavicon": true, "openInNewTab": false, "showClock": true, "showWeather": true, "language": "ru" }, "bingCache": { "date": "2026-07-30", "url": "...", "copyright": "..." }, "weatherCaches": { "<cityId>": { "ok": true, "symbol": "clearsky_day", "tempC": 12.4, ... } } }