Да, по сути получается: хоткей, голос, хоткей, клик. Очень быстро. Микрофон всегда рядом с клавиатурой, один из основных рабочих инструментов в последние полгода.
Я публикую комментарии и переписываюсь через голос: записываю мысли в Handy, автоматически конвертирую в текст и вставляю в Beetroot, где AI исправляет грамматику и улучшает стиль. Это мои мысли, а не «чистый» ИИ: я наговорил содержание, потом корректирую ошибки и читаю, как должно звучать правильно. Метод помогает практиковать языки — русский у меня для семьи, а ежедневно я пользуюсь английским, немецким и иногда тайским — поэтому важно видеть исправления и улучшать грамматику. Кроме того, это экономит время: голосом можно передать большой объём, затем удалить междометия и сделать текст читабельным. Наконец, это уважение к аудитории — публиковать грамотные тексты на всех используемых языках.
Спасибо, буду тестировать разные решения из комментариев на реальных данных. Потом обязательно напишу о результатах. Однозначно буду переходить с JS на Rust, а дальше - вопрос выбора и сравнения разных алгоритмов. Тема оказалась очень интересной.
Почему JS, а не Rust? Данные уже живут в JS — загружены из SQLite в React state для рендеринга. Поиск на Rust значил бы IPC round-trip на каждый keystroke. При 0.76 мс на поиск в JS сам IPC overhead может оказаться больше выигрыша. Но вы правы — при росте базы до 10K+ имеет смысл перенести поиск в Rust и отдавать только отфильтрованные ID.
nucleo и frizbee — спасибо, не знал про них. nucleo особенно интересен — Rust-native, быстрый, и если переносить поиск на бэкенд, это первый кандидат. Smith-Waterman тоже посмотрю.
Fuzzy — non-problem? Отчасти согласен, и на практике я к этому пришёл: в текущей версии fuzzy запускается только когда точные фазы нашли < 5 результатов. Первые 4 фазы — это именно то, что вы описали: подстроки + case insensitive + любой порядок токенов + границы слов. Fuzzy — страховка на самом дне выдачи. Разница в 5-й фазе, которая на практике срабатывает редко.
frecency — отличная идея. Сейчас тайбрейкер — last_used, но трекать «какой результат юзер реально выбрал после поиска» и бустить его в будущем — это следующий уровень. Записал.
Асинхронное формирование выдачи — при 0.76 мс на весь пайплайн пользователь не увидит разницы между синхронным и progressive-обновлением. Но если перенести на Rust + nucleo с базой побольше, то да, streaming результатов через IPC был бы правильным паттерном. Ради такой информации и публиковал статью.
Инвертированный индекс — первое что приходит в голову для текстового поиска. Для моего кейса (1000–2000 записей in-memory, 0.76 мс на линейном проходе) пока overhead на поддержание индекса не оправдан, но если база вырастет до 10K+ — это первое куда полезу.
Вопрос, который меня мучает: инвертированный индекс решает задачу найти быстро, но моя главная боль была отранжировать правильно — отличить совпадение в контенте от совпадения в метаданных окна. Вы сталкивались с ранжированием поверх инвертированного индекса на клиенте?
Суффиксное дерево для подстрочного поиска — тоже думал, но остановила память. Средняя запись 200–500 символов, бывают 5K+. Есть ли компактные реализации которые на таких объёмах имеют смысл?
Спасибо, чуток поизучал тему глубже, думаю, что вы правы. Ситуация по факту лучше, чем я думал.
Сейчас Beetroot блокирует сетевые диски и UNC-пути при выборе data directory. Решение было defensive, но с учётом вашего разбора про SMBv3/NFS блок избыточен, заменю на предупреждение.
Облачные папки (OneDrive, Dropbox, Google Drive) - это отдельная история: там sync engine копирует файлы между устройствами без координации блокировок. Для них показываю предупреждение, а не блокировку.
По async I/O - всё через Tauri IPC, UI не блокируется при медленном диске. Плюс бэкапы через SQLite Backup API и автовосстановление при повреждении, так что даже в худшем случае данные не теряются.
Да, AI полностью опционален, он не встроен в ядро приложения и не требует никаких ключей для работы. Beetroot из коробки работает как обычный clipboard manager: история, поиск, starred clips, OCR, темы, трансформации текста (upper/lower/trim/title case).
AI-фичи появляются только если вы сами зайдёте в настройки и настроите провайдера. Без этого никаких запросов наружу, никаких зависимостей от облачных сервисов.
Единственное, что ходит в интернет без AI - это проверка обновлений (раз при запуске, GitHub API). Но и это отключается в настройках. После этого приложение живёт полностью оффлайн.
Отдельная опция в инсталляторе не нужна, потому что AI-код - это буквально один fetch к API, который никогда не вызывается без явной настройки. Ни бинарников, ни моделей, ни SDK в поставке нет.
Теоретически да, Tauri v2 поддерживает Linux, и основная логика (React + SQLite) кросс-платформенная. Но Windows-специфичного кода довольно много: clipboard monitoring через Win32 API, OCR через WinRT, определение раскладки клавиатуры, извлечение иконок приложений, window management (always-on-top, follow cursor, multi-monitor DPI). Это всё пришлось бы переписывать на Linux-аналоги (X11/Wayland, Tesseract, и т.д.).
Пока в приоритете Windows. Но Linux-порт вполне реален, архитектура позволяет.
В текущей версии Beetroot умеет трансформировать текст через AI — OpenAI и локальные модели (Ollama, LM Studio). В ближайшем релизе добавится Gemini. Но это только начало — вот что планирую дальше:
Глобальные AI-хоткеи. Выделяешь текст в любом приложении → шоткат → результат обратно на место. Исправление ошибок, перевод, переформатирование — без открытия окна, без потери содержимого буфера.
Webhook-пайплайны. Отправка клипа по хоткею в n8n, Make, Zapier или любой HTTP-эндпоинт. Результат возвращается как новый клип.
Логика такая: ALT+P и ALT+F - это локальные хоткеи, то есть работают когда окно Beetroot уже открыто. Но переключить режим нужно только один раз, настройка сохраняется. После этого каждый раз, когда вызываете Beetroot глобальным хоткеем, окно будет появляться рядом с курсором (режим Follow Cursor) или оставаться поверх всех окон (Pinned).
Переключить можно и мышкой - в футере две иконки рядом с шестерёнкой.
Размер шрифта уже настраивается: Settings → Appearance → Font Size (6 вариантов от 11 до 18px). Там же можно выбрать шрифт.
Первое уже есть. В Beetroot три режима позиционирования окна: по центру экрана (по умолчанию), следование за курсором и закреплённое окно поверх всех. Переключаются хоткеем или кнопкой в футере. В режиме Follow Cursor окно появляется прямо рядом с мышью, можно выбрать нужный клип и вставить, не отводя взгляда.
Портабельная версия: хороший запрос, пока только installer, но технически ничего не мешает сделать portable-сборку.
По паролям: сейчас Beetroot автоматически пропускает записи из парольных менеджеров (детектирует по специфичным форматам буфера обмена). Идея сделать это опциональным логична. Кому-то может быть удобно наоборот сохранять. Учту.
SQLite координирует доступ через файловые блокировки (fcntl на Unix, LockFileEx на Windows). Сетевые файловые системы реализуют эти блокировки ненадёжно. в FAQ SQLite прямо сказано: "fcntl() file locking is broken on many NFS implementations", а про Windows: "file locking of network files is very buggy and is not dependable". В https://www.sqlite.org/howtocorrupt.html описан механизм: если блокировки работают некорректно, два процесса могут одновременно писать в базу, что ведет к corruption.
В Beetroot база всегда локальная, но пользователи иногда выбирают синхронизируемую папку (OneDrive, Dropbox, Google Drive) как data directory, а это даже хуже, чем NFS: облачные сервисы синхронизируют файлы, а не блокировки, и два устройства могут писать в одну базу не подозревая друг о друге. Поэтому добавил детекцию таких папок и предупреждение при выборе пути.
Спасибо! На macOS пользовался Paste, по сути он и стал главным вдохновением для Beetroot. Ещё Alfred с его Clipboard History. Но Paste — это платная подписка, Alfred Powerpack тоже платный, и оба только Mac. Захотелось сделать что-то похожее для Windows, прежде всего для самого себя, чтобы пользоваться каждый день.
Отличная идея, спасибо за ссылку! Это уже в роадмапе — глобальные шоткаты для AI-действий без открытия окна. Сейчас кастомные промпты работают через контекстное меню, но в целом весь этот блок буду переписывать.
Помимо встроенных AI-провайдеров, думаю сделать опцию отправки по шоткату webhook в пайплайн (например n8n) и получение результата обратно в виде нового клипа — чтобы можно было строить любые цепочки обработки снаружи.
Я ничего против Electron не имею — это отличный инструмент, на нём написано огромное количество хорошего софта. Просто для себя сделал такой выбор. Как человеку, который писал код для i286 во дворце пионеров, мне морально тяжело видеть инсталлятор на 130 МБ и 300 МБ в трее. Наверное, профессиональная деформация, гонюсь за оптимизацией там, где можно и не гнаться.
Спасибо! Как раз в свежей версии (v1.4.0) это прикрутил — Ollama, LM Studio и любой кастомный OpenAI-compatible endpoint. Оказалось проще, чем я ожидал, но не без подводных камней: Ollama и LM Studio используют разные эндпоинты для списка моделей (/v1/models vs /api/tags), а reasoning-модели оборачивают ответ в теги, которые нужно вырезать. Плюс пришлось пробрасывать запросы через Rust IPC, потому что Tauri'шный CSP блокирует fetch на localhost из WebView — это было самое неочевидное.
Сейчас добавляю Gemini, дальше другие провайдеры. Технически подключить несложно, основное время уходит на тестирование.
По OCR — нативный Windows API (WinRT OcrEngine) работает мгновенно и без зависимостей, но качество на сложных изображениях ограничено (точно хуже, чем на MacOS). Есть идея сделать автоматическое распознавание текста на всех картинках в фоне — тогда по ним можно будет искать так же, как по обычному тексту.
Кстати, в версии, которая сейчас готовится к релизу, поиск уже работает не только по содержимому, но и по заголовкам окон-источников. Например, можно ввести название Telegram-канала — и найдутся все сообщения, скопированные оттуда, потому что Telegram подставляет название чата в title окна.
Да, по сути получается: хоткей, голос, хоткей, клик. Очень быстро. Микрофон всегда рядом с клавиатурой, один из основных рабочих инструментов в последние полгода.
Я публикую комментарии и переписываюсь через голос: записываю мысли в Handy, автоматически конвертирую в текст и вставляю в Beetroot, где AI исправляет грамматику и улучшает стиль. Это мои мысли, а не «чистый» ИИ: я наговорил содержание, потом корректирую ошибки и читаю, как должно звучать правильно. Метод помогает практиковать языки — русский у меня для семьи, а ежедневно я пользуюсь английским, немецким и иногда тайским — поэтому важно видеть исправления и улучшать грамматику. Кроме того, это экономит время: голосом можно передать большой объём, затем удалить междометия и сделать текст читабельным. Наконец, это уважение к аудитории — публиковать грамотные тексты на всех используемых языках.
Спасибо, буду тестировать разные решения из комментариев на реальных данных. Потом обязательно напишу о результатах. Однозначно буду переходить с JS на Rust, а дальше - вопрос выбора и сравнения разных алгоритмов. Тема оказалась очень интересной.
Спасибо, тут много ценного. По пунктам:
Почему JS, а не Rust? Данные уже живут в JS — загружены из SQLite в React state для рендеринга. Поиск на Rust значил бы IPC round-trip на каждый keystroke. При 0.76 мс на поиск в JS сам IPC overhead может оказаться больше выигрыша. Но вы правы — при росте базы до 10K+ имеет смысл перенести поиск в Rust и отдавать только отфильтрованные ID.
nucleo и frizbee — спасибо, не знал про них. nucleo особенно интересен — Rust-native, быстрый, и если переносить поиск на бэкенд, это первый кандидат. Smith-Waterman тоже посмотрю.
Fuzzy — non-problem? Отчасти согласен, и на практике я к этому пришёл: в текущей версии fuzzy запускается только когда точные фазы нашли < 5 результатов. Первые 4 фазы — это именно то, что вы описали: подстроки + case insensitive + любой порядок токенов + границы слов. Fuzzy — страховка на самом дне выдачи. Разница в 5-й фазе, которая на практике срабатывает редко.
frecency — отличная идея. Сейчас тайбрейкер — last_used, но трекать «какой результат юзер реально выбрал после поиска» и бустить его в будущем — это следующий уровень. Записал.
Асинхронное формирование выдачи — при 0.76 мс на весь пайплайн пользователь не увидит разницы между синхронным и progressive-обновлением. Но если перенести на Rust + nucleo с базой побольше, то да, streaming результатов через IPC был бы правильным паттерном. Ради такой информации и публиковал статью.
Инвертированный индекс — первое что приходит в голову для текстового поиска. Для моего кейса (1000–2000 записей in-memory, 0.76 мс на линейном проходе) пока overhead на поддержание индекса не оправдан, но если база вырастет до 10K+ — это первое куда полезу.
Вопрос, который меня мучает: инвертированный индекс решает задачу найти быстро, но моя главная боль была отранжировать правильно — отличить совпадение в контенте от совпадения в метаданных окна. Вы сталкивались с ранжированием поверх инвертированного индекса на клиенте?
Суффиксное дерево для подстрочного поиска — тоже думал, но остановила память. Средняя запись 200–500 символов, бывают 5K+. Есть ли компактные реализации которые на таких объёмах имеют смысл?
Спасибо, чуток поизучал тему глубже, думаю, что вы правы. Ситуация по факту лучше, чем я думал.
Сейчас Beetroot блокирует сетевые диски и UNC-пути при выборе data directory. Решение было defensive, но с учётом вашего разбора про SMBv3/NFS блок избыточен, заменю на предупреждение.
Облачные папки (OneDrive, Dropbox, Google Drive) - это отдельная история: там sync engine копирует файлы между устройствами без координации блокировок. Для них показываю предупреждение, а не блокировку.
По async I/O - всё через Tauri IPC, UI не блокируется при медленном диске. Плюс бэкапы через SQLite Backup API и автовосстановление при повреждении, так что даже в худшем случае данные не теряются.
Да, AI полностью опционален, он не встроен в ядро приложения и не требует никаких ключей для работы. Beetroot из коробки работает как обычный clipboard manager: история, поиск, starred clips, OCR, темы, трансформации текста (upper/lower/trim/title case).
AI-фичи появляются только если вы сами зайдёте в настройки и настроите провайдера. Без этого никаких запросов наружу, никаких зависимостей от облачных сервисов.
Единственное, что ходит в интернет без AI - это проверка обновлений (раз при запуске, GitHub API). Но и это отключается в настройках. После этого приложение живёт полностью оффлайн.
Отдельная опция в инсталляторе не нужна, потому что AI-код - это буквально один fetch к API, который никогда не вызывается без явной настройки. Ни бинарников, ни моделей, ни SDK в поставке нет.
Теоретически да, Tauri v2 поддерживает Linux, и основная логика (React + SQLite) кросс-платформенная. Но Windows-специфичного кода довольно много: clipboard monitoring через Win32 API, OCR через WinRT, определение раскладки клавиатуры, извлечение иконок приложений, window management (always-on-top, follow cursor, multi-monitor DPI). Это всё пришлось бы переписывать на Linux-аналоги (X11/Wayland, Tesseract, и т.д.).
Пока в приоритете Windows. Но Linux-порт вполне реален, архитектура позволяет.
В текущей версии Beetroot умеет трансформировать текст через AI — OpenAI и локальные модели (Ollama, LM Studio). В ближайшем релизе добавится Gemini. Но это только начало — вот что планирую дальше:
Глобальные AI-хоткеи. Выделяешь текст в любом приложении → шоткат → результат обратно на место. Исправление ошибок, перевод, переформатирование — без открытия окна, без потери содержимого буфера.
Webhook-пайплайны. Отправка клипа по хоткею в n8n, Make, Zapier или любой HTTP-эндпоинт. Результат возвращается как новый клип.
Примеры:
Скопировал ссылку → webhook достаёт заголовок, описание, OG-картинку → готовый markdown в буфере
Скопировал текст → webhook прогоняет через DeepL → перевод обратно
Скопировал код → webhook форматирует/линтит → результат в буфере
Скопировал email → webhook парсит контакт → добавляет в CRM
Скопировал данные → webhook пишет в Google Sheets / Notion
По сути — clipboard как универсальный input для любой автоматизации.
Фантазировать можно бесконечно.
Буду рад идеям.
Логика такая: ALT+P и ALT+F - это локальные хоткеи, то есть работают когда окно Beetroot уже открыто. Но переключить режим нужно только один раз, настройка сохраняется. После этого каждый раз, когда вызываете Beetroot глобальным хоткеем, окно будет появляться рядом с курсором (режим Follow Cursor) или оставаться поверх всех окон (Pinned).
Переключить можно и мышкой - в футере две иконки рядом с шестерёнкой.
Размер шрифта уже настраивается: Settings → Appearance → Font Size (6 вариантов от 11 до 18px). Там же можно выбрать шрифт.
Первое уже есть. В Beetroot три режима позиционирования окна: по центру экрана (по умолчанию), следование за курсором и закреплённое окно поверх всех. Переключаются хоткеем или кнопкой в футере. В режиме Follow Cursor окно появляется прямо рядом с мышью, можно выбрать нужный клип и вставить, не отводя взгляда.
Портабельная версия: хороший запрос, пока только installer, но технически ничего не мешает сделать portable-сборку.
По паролям: сейчас Beetroot автоматически пропускает записи из парольных менеджеров (детектирует по специфичным форматам буфера обмена). Идея сделать это опциональным логична. Кому-то может быть удобно наоборот сохранять. Учту.
SQLite координирует доступ через файловые блокировки (fcntl на Unix, LockFileEx на Windows). Сетевые файловые системы реализуют эти блокировки ненадёжно. в FAQ SQLite прямо сказано: "fcntl() file locking is broken on many NFS implementations", а про Windows: "file locking of network files is very buggy and is not dependable". В https://www.sqlite.org/howtocorrupt.html описан механизм: если блокировки работают некорректно, два процесса могут одновременно писать в базу, что ведет к corruption.
В Beetroot база всегда локальная, но пользователи иногда выбирают синхронизируемую папку (OneDrive, Dropbox, Google Drive) как data directory, а это даже хуже, чем NFS: облачные сервисы синхронизируют файлы, а не блокировки, и два устройства могут писать в одну базу не подозревая друг о друге. Поэтому добавил детекцию таких папок и предупреждение при выборе пути.
Спасибо! На macOS пользовался Paste, по сути он и стал главным вдохновением для Beetroot. Ещё Alfred с его Clipboard History. Но Paste — это платная подписка, Alfred Powerpack тоже платный, и оба только Mac. Захотелось сделать что-то похожее для Windows, прежде всего для самого себя, чтобы пользоваться каждый день.
Отличная идея, спасибо за ссылку! Это уже в роадмапе — глобальные шоткаты для AI-действий без открытия окна. Сейчас кастомные промпты работают через контекстное меню, но в целом весь этот блок буду переписывать.
Помимо встроенных AI-провайдеров, думаю сделать опцию отправки по шоткату webhook в пайплайн (например n8n) и получение результата обратно в виде нового клипа — чтобы можно было строить любые цепочки обработки снаружи.
Я ничего против Electron не имею — это отличный инструмент, на нём написано огромное количество хорошего софта. Просто для себя сделал такой выбор. Как человеку, который писал код для i286 во дворце пионеров, мне морально тяжело видеть инсталлятор на 130 МБ и 300 МБ в трее. Наверное, профессиональная деформация, гонюсь за оптимизацией там, где можно и не гнаться.
Спасибо! Как раз в свежей версии (v1.4.0) это прикрутил — Ollama, LM Studio и любой кастомный OpenAI-compatible endpoint. Оказалось проще, чем я ожидал, но не без подводных камней: Ollama и LM Studio используют разные эндпоинты для списка моделей (/v1/models vs /api/tags), а reasoning-модели оборачивают ответ в теги, которые нужно вырезать. Плюс пришлось пробрасывать запросы через Rust IPC, потому что Tauri'шный CSP блокирует fetch на localhost из WebView — это было самое неочевидное.
Сейчас добавляю Gemini, дальше другие провайдеры. Технически подключить несложно, основное время уходит на тестирование.
По OCR — нативный Windows API (WinRT OcrEngine) работает мгновенно и без зависимостей, но качество на сложных изображениях ограничено (точно хуже, чем на MacOS). Есть идея сделать автоматическое распознавание текста на всех картинках в фоне — тогда по ним можно будет искать так же, как по обычному тексту.
Кстати, в версии, которая сейчас готовится к релизу, поиск уже работает не только по содержимому, но и по заголовкам окон-источников. Например, можно ввести название Telegram-канала — и найдутся все сообщения, скопированные оттуда, потому что Telegram подставляет название чата в title окна.