Skip to content

FLEXIY0/todo

Repository files navigation

Simple Todo — vanilla js · zero dependencies · pixel zen

Минималистичный todo-лист для Android, написанный на чистом JavaScript.
Ни фреймворков, ни зависимостей, ни сборщика — только ванильный JS, CSS и немного любви к деталям.

Build APK Vanilla JS, zero dependencies Capacitor 8 Android 7.0+


🎬 Демо

Живая демонстрация: зачёркивание, подзадачи, перетаскивание категорий, смена тем

зачёркивание → подзадачи → листание пространств → история правок → смена темы

✨ Скриншоты

Classic OLED Anthropic Подзадачи
Classic theme OLED theme Anthropic theme Subtasks
Янтарь на тёмном Чистый чёрный Айвори и терракота Вложенный экран

🌿 Возможности

  • Пространства — листай влево/вправо между To-Do, Wishlist и общим пространством с плавной анимацией переворота книжной страницы, полностью подконтрольной пальцу. Пространства можно переименовывать, выключать, добавлять новые и прятать подписи вкладок (останется точка) — всё в настройках
  • Общее пространство — две независимые синхронизируемые доски (To-Do и Wishlist) и два канала связи: напрямую телефон-телефон через WebRTC, плюс «почтовый ящик» — зашифрованный снапшот на публичном MQTT-брокере, так что телефоны не обязаны быть онлайн одновременно. Один тап «Invite» — код уже в буфере, на втором телефоне «Join» — и доски сливаются: объединение + побеждает последняя правка
  • История правок — фиксируется всё: добавления, правки текста, выполнения, удаления, очистки, и даже отладочные события синхронизации. Любое удалённое возвращается одной кнопкой , правка текста откатывается. Глубина хранения настраивается прямо там (50 / 200 / 1000 / всё)
  • Дерево подзадач — в вишлисте вложенность видна сразу: подзадачи раскрыты деревом с ветками ├ └; в туду — минималистичные полоски. Переключается в настройках для каждого пространства
  • Экспорт в буфер — задача, категория или всё пространство копируются как markdown-чеклист
  • Импорт из буфера — вставь скопированный список в любое пространство: открывается окно детального редактирования (поправить/убрать лишнее перед вставкой), парсер прощает неровный markdown из других источников
  • Системный жест «назад» — закрывает меню/экраны как отмена, а не сворачивает приложение
  • Категории и задачи — редактируемый заголовок, счётчики выполнено/всего, плавная анимация зачёркивания, которую можно отменить на полпути
  • Подзадачи — один уровень вложенности: под задачей появляются минималистичные полоски-индикаторы, тап открывает вложенный экран, а родительская задача завершается сама, когда закрыты все подзадачи
  • Перетаскивание категорий — удержи заголовок категории и тащи: соседи плавно расступаются
  • Четыре темыClassic (янтарь на тёмно-коричневом), OLED (чистый чёрный для экономии экрана), Anthropic (светлая, айвори с терракотой) и Anthropic Dark (тёплый тёмно-серый с антикварным терракотовым акцентом). Вынесены в отдельный экран «Themes» из бокового меню
  • Настройка шрифтов — размер текста (Small / Medium / Large) и начертание (системный / моноширинный / с засечками) в настройках; применяется ко всему списку
  • Всё на жестах — ни одной лишней кнопки в интерфейсе
  • Автосохранение — состояние живёт в localStorage, переживает перезапуск
  • Без аналитики и разрешений — сеть используется только для синхронизации общего пространства, и только когда ты сам её включаешь

👆 Жесты

Жест Действие
Свайп влево / вправо Перелистнуть на соседнее пространство (как страницу книги)
Свайп вправо на главной странице Открыть боковое меню (на весь экран, не только от края)
Свайп вправо во вложенном экране Вернуться назад (тем же перелистыванием страницы)
Тап по задаче Выполнить / вернуть (анимация туда и обратно)
Двойной тап по задаче Открыть вложенный экран с подзадачами
Долгое нажатие на задачу Меню: выполнить · подзадачи · изменить · удалить
Удержать заголовок категории и тащить Поменять категории местами
Удержать заголовок категории и отпустить Меню: переименовать · очистить выполненные · удалить
Тройной тап по заголовку категории Убрать выполненные в этой категории
Тройной тап по пустому месту Убрать все выполненные в пространстве
Долгое нажатие на пустое место Добавить категорию / экспорт / вставка
Тап по заголовку приложения Переименовать его во что угодно

📦 Установка и тест

  • APK — готовый лежит в Releases. Универсальный: приложение это Capacitor WebView без нативных библиотек, поэтому одна сборка работает на armv7, arm64 и x86.
  • В браузере (без сборки APK) — приложение это чистый веб, так что любую правку можно проверить прямо в браузере, в том числе на телефоне. После каждого пуша в main GitHub Actions публикует свежую версию на GitHub Pages — открывай ссылку из вкладки Settings → Pages и тестируй. Одноразовая настройка: Settings → Pages → Source: GitHub Actions.

🔨 Сборка

Веб-версия — сборки нет в принципе, просто открой index.html (или раздай по сети, чтобы зайти с телефона):

npx serve .

APK локально:

npm ci
npm run build            # копирует веб-файлы в www/
npx cap sync android     # синхронизирует их в Android-проект
cd android && ./gradlew assembleRelease
# → android/app/build/outputs/apk/release/app-release.apk

APK через CI — пушни тег, GitHub Actions соберёт и опубликует релиз сам:

git tag v1.1 && git push origin v1.1

🎨 Иконка

Иконка — пиксельный листик — не нарисована, а сгенерирована кодом: scripts/generate-icon.js содержит PNG-энкодер, написанный с нуля поверх zlib (никаких зависимостей), и рисует листик из сетки 16×16:

................
.......L........
......LLL.......
.....LLDLL......
....LHLDLLL.....
...LHLLDLLLL....
...LHLLDLLLL....
..LLLLLDLLLLL...
..LLLLLDLLLLL...
..LLLLLDLLLLL...
...LLLLDLLLL....
...LLLLDLLLL....
....LLLDLLL.....
.....LLDLL......
.......D........
......DD........

Один запуск node scripts/generate-icon.js пересоздаёт icon.png и все лаунчер-иконки Android (обычные, круглые и adaptive — для всех плотностей экрана). Хочешь другую иконку — поменяй буквы в сетке и перезапусти.

Анимированный баннер в шапке этого README (docs/banner.svg) собирается из той же сетки скриптом scripts/generate-banner.js — каждый квадратик появляется по очереди, потом листик тихонько покачивается.

🧠 Как устроено

index.html   — разметка, ни строчки логики
ui.js        — «хром»: шторка, bottom sheet, диалог, темы, жесты
app.js       — состояние, рендер, пространства, журнал, flip-движок
sync.js      — P2P-синхронизация общего пространства (WebRTC)
styles.css   — все стили и keyframe-анимации
vendor/      — единственная вендоренная библиотека: peerjs
android/     — Capacitor-проект для сборки APK

Пара решений, которыми я доволен:

  • Полный ре-рендер, кроме анимаций. render() каждый раз пересобирает DOM целиком — просто и предсказуемо. Но во время анимации зачёркивания рендер не вызывается: классы переключаются точечно на живых элементах, поэтому анимацию нельзя «сбить», а повторный тап мгновенно разворачивает её в обратную сторону.
  • Два модальных примитива на всё. Любое меню — это openSheet(), любой ввод — openDialog(). Больше модалок в приложении нет.
  • Завершение родителя — производное состояние. Задачу с подзадачами нельзя отметить руками: её done всегда вычисляется из подзадач. Меньше способов получить противоречивое состояние.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages