Skip to content

Latest commit

 

History

93 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tabula

Release Site

Tabula — расширение для браузера, которое превращает новую вкладку в электронную таблицу. Закладки раскладываются по ячейкам сетки, как в Excel: со своими листами, темами, фоном, часами и погодой.

  • Работает в Chromium-браузерах (Chrome, Edge, Brave, Opera, Vivaldi…) и Firefox 140+.
  • Интерфейс — на русском, в настройках переключается на английский.
  • Без подписок, без телеметрии — все данные хранятся локально в вашем браузере.
  • Автор: withersky.

🌐 Сайт-визитка с кнопками скачивания: https://tabula.withersky.workers.dev/

Установка

Chrome / Edge / Brave / Opera / Vivaldi

  1. Скачайте архив Chrome со страницы релизов.
  2. Распакуйте его в любую папку.
  3. Откройте chrome://extensions (или edge://extensions) и включите «Режим разработчика».
  4. Нажмите «Загрузить распакованное расширение» и выберите распакованную папку.
  5. Откройте новую вкладку — появится таблица. Настройки — иконка ⚙ в правом верхнем углу.

Firefox 140+

  1. Скачайте архив Firefox со страницы релизов.
  2. Распакуйте его в любую папку.
  3. Откройте about:debugging#/runtime/this-firefox.
  4. Нажмите «Загрузить временное дополнение…» и выберите файл manifest.json из папки.

Если другое расширение уже переопределяет новую вкладку, отключите его — браузер использует только одно такое расширение одновременно.

Яндекс Браузер

Яндекс Браузер не позволяет переопределять страницу новой вкладки через chrome_url_overrides, поэтому схема запуска другая — расширение «живёт» в панели расширений и открывает таблицу по клику на иконку:

  1. Скачайте архив Yandex со страницы релизов.
  2. Распакуйте его в любую папку.
  3. Откройте browser://extensions и включите «Режим разработчика» (внизу страницы).
  4. Нажмите «Загрузить расширение» и выберите распакованную папку.
  5. Закрепите иконку Tabula в панели расширений (через меню «Показать на панели»), чтобы она всегда была под рукой.
  6. Клик по иконке открывает 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, он:

  1. Проверяет, что поле "version" в src/manifest.json и src/manifest.firefox.json совпадает с X.Y.Z (страховка от рассинхрона).
  2. Собирает оба варианта через ./build.sh all и упаковывает в tabula-chrome-vX.Y.Z.zip и tabula-firefox-vX.Y.Z.zip.
  3. Ставит git-тег vX.Y.Z и публикует GitHub Release с архивами и полным текстом commit message в качестве release notes.

Обычные коммиты (без префикса vX.Y.Z) workflow тихо пропускает.

Процедура релиза:

  1. Поднимите версию в обоих манифестах (src/manifest.json, src/manifest.firefox.json).
  2. Коммит, первая строка которого выглядит так:
v1.2.3 - короткое описание релиза

- подробности изменения 1
- подробности изменения 2
  1. 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 файл он отклонит). Поэтому после того, как заявка пройдёт ревью в Центре разработчиков:

  1. Скачайте подписанный .xpi из Центра разработчиков.
  2. Переименуйте его в tabula-firefox-v1.2.3.xpi (без суффикса -unsign).
  3. Загрузите в тот же релиз v1.2.3 на GitHub (через интерфейс Releases → Attach a file или командой gh release upload). release.yml сам дописывает update_link на этот подписанный .xpi в site/updates.json (без update_hash — self-hosted update_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.

Сайт-визитка и Cloudflare Workers

Папка 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.

Автодеплой в Cloudflare Workers через Git-интеграцию

Сайт деплоится автоматически при каждом пуше в подключённую ветку /site — через Workers Builds (нативная Git-интеграция Cloudflare, как у Pages, но для обычного Worker).

Конфигурация лежит в wrangler.toml: name = "tabula-plugin", assets.directory = "site" — то есть воркер отдаёт статическую папку site/ как есть (сборка не нужна, build-шаг пустой).

Подключение репозитория (делается один раз в дашборде Cloudflare): см. пошаговые пути в CLOUDFLARE-SETUP.md. Кратко: Worker tabula-pluginSettings → Builds → Connect Git repository, ветка main, Build command пусто, Deploy command wrangler deploy, и рекомендуется поставить Path filter site/**.

❄️ Старый воркфлоу GitHub Pages (.github/workflows/site.yml) заморожен — из него убран push-триггер и добавлен guard inputs.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:

{
  "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, ... } }
}

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_code PCLI в open-meteo) поле timezone отсутствует — поэтому страны нельзя добавить как город погоды/часов (иначе время считалось бы в UTC); при поиске они отфильтровываются.
  • Синхронизация между устройствами не предусмотрена — используйте экспорт/импорт JSON.
  • Автообновление Firefox работает только через update_url на подписанный .xpi (см. раздел «Релизы»); неподписанные сборки пользователям не обновляются автоматически.
  • Требования: Manifest V3, Chrome 108+; Firefox 140+ (в Firefox-сборке используется background.scripts).

Releases

Contributors

Languages