invisible-ai: AI-оверлей, который в Linux правда можно спрятать от демонстрации экрана
DraftОкно, которое не попадает в захват экрана, нужно не только для подсказок на собеседовании в духе Cluely: менеджер паролей, история буфера обмена — всё, что не должно оказаться на общем экране. В Linux это не невозможно, а зависит от композитора. Как это сделано в invisible-ai (Tauri v2 + Rust) под niri и почему в X11 так не выйдет никогда.
Cluely даже не скрывает, зачем он нужен: это AI-оверлей, который подсказывает ответы на собеседовании и при этом «undetectable by design» — собеседник по ту сторону демонстрации экрана его не видит. Как ни относись к такому сценарию, он навёл меня на другой вопрос: почему в Linux вообще нет такого примитива?
Ведь окно, которое не попадает в захват экрана, — это не инструмент для списывания, а инструмент приватности, и нужен он чаще, чем кажется:
- Менеджер паролей. Идёт созвон, нужен пароль, а окно Bitwarden не должно попасть на общий экран. Расширение в браузере тоже не спасает: один случайный клик по иконке с глазом — и пароль уже в трансляции.
- История буфера обмена. Вставляешь токен, ключ, внутреннюю ссылку — из пикера, который, конечно же, открыт на экране, пока ты выбираешь.
- Коды 2FA, внутренние дашборды, терминал с доступами к стейджингу в заголовке. Всего, что «на моём мониторе можно, в Zoom нельзя», больше, чем кажется, и демонстрация экрана показывает это всё разом.
На macOS это один вызов API (NSWindowSharingNone), на Windows — другой
(WDA_EXCLUDEFROMCAPTURE). Под Linux Cluely не выходит вовсе. Открытые клоны, у
которых есть сборки под Linux, честно об этом предупреждают: в README
OpenCluely прямо написано, что на Linux
setContentProtection молча ничего не делает и оверлей увидят все, кому вы
показываете экран, а невидимость у Pluely
держится на поведении композитора, которое никто не гарантирует.
Так что, надёжный невидимый для захвата оверлей в Linux невозможен? Нет. Но честный ответ зависит от композитора, и этот пост — про него. А заодно про приложение, которое я вокруг этого ответа построил: invisible-ai — AI-оверлей для Linux на Tauri v2 + Rust, который видит экран и слышит созвон. Я каждый день пользуюсь им на i3 и niri.
Чем Linux отличается (и почему не всё потеряно)
Аналога WDA_EXCLUDEFROMCAPTURE в Linux нет по архитектурной причине. В Windows и
macOS захватом экрана владеет сама ОС, поэтому она и может дать окну флаг «не
захватывать меня». В Linux то, кто захватывает экран, зависит от дисплейного сервера
— и исключать окна в принципе умеет только один из двух:
- X11: не сложно, а невозможно. Разрешений на захват там нет, а значит, и
отказываться не от чего: любой клиент читает пиксели любого окна через
XGetImage. Экран — одна общая картинка; как только оверлей в неё попал, все, кто захватывает экран (Zoom, OBS, Discord), видят одно и то же. Никакой трюк на уровне оконного менеджера этого не изменит. - Wayland: захватом владеет композитор, значит, он и может исключать. Некоторые
уже умеют:
- у niri есть
block-out-from "screen-capture"в правилах окон и слоёв; - в KDE Plasma 6.6+ у KWin появилось действие «Hide from ScreenCast» для окна;
- в GNOME/Mutter встроенного решения нет.
- у niri есть
Невидимый для захвата оверлей в Linux возможен, если ваш композитор это поддерживает, а приложение, которое обещает это везде, о чём-то умалчивает. Разумный подход такой: определить композитор, использовать его механизм и честно показывать пользователю, скрыт он сейчас или нет.
Что умеет invisible-ai
Полупрозрачная панель поверх рабочего стола, которой целиком управляешь с клавиатуры:
- По умолчанию клики проходят сквозь неё. Прозрачная, всегда сверху, на всех
рабочих столах, без значка в панели задач — работаешь прямо сквозь неё.
interactделает её кликабельной и забирает фокус (фиолетовая рамка) — для настроек или вопроса текстом; при закрытии всё возвращается обратно. askделает скриншот монитора, на котором висит оверлей, отправляет его вместе с вопросом любому OpenAI-совместимому провайдеру (NanoGPT, OpenRouter, OpenCode Zen, Ollama — подойдёт любая модель с vision) и выводит ответ в панель по мере генерации: Markdown с подсветкой кода. Ключи API берутся из переменной окружения или shell-команды, в конфиге их нет, а сам конфиг перечитывается при каждом запросе.listenвключает живую расшифровку: локальный whisper.cpp распознаёт ваш микрофон («Me») и системный звук («Them»), которыеpw-recordпишет раздельно. На вопросы из расшифровки оверлей отвечает сам, с экраном и последними репликами в качестве контекста; строки с микрофона, которые просто повторяют собеседника, отбрасываются. Звук никуда не уходит с машины — наружу идут только запросы к модели.- Всё управление — через Unix-сокет. Запущенное приложение слушает
$XDG_RUNTIME_DIR/invisible-ai.sock, а тот же бинарник с аргументом работает как клиент. Любая команда запускает приложение, если оно ещё не запущено, так что один хоткей в WM — это сразу и лаунчер, и пульт:
# i3bindsym $mod+backslash exec invisible-ai toggle # показать / скрыть (и запустить)bindsym $mod+Ctrl+backslash exec invisible-ai prompt # набрать вопросbindsym $mod+Mod1+backslash exec invisible-ai listen # голос вкл / выклbindsym $mod+Shift+backslash exec invisible-ai interactВ X11 всё это уже работает: прозрачность даёт picom, оконные трюки — GTK. Окно типа
UTILITY в i3 становится плавающим; позиция и область ввода применяются в момент
отображения, потому что всё, что запрошено раньше, чем i3 начал управлять окном, он
игнорирует; focus_on_map выключен, поэтому появившийся оверлей никогда не крадёт
фокус. Кроме того, invisible-ai не пускает панель в свои собственные скриншоты, чтобы
модель не читала обратно свои прошлые ответы: у компактной панели её область
закрашивается на картинке, а полноэкранная на ~150 мс уходит в нулевую прозрачность
через picom, пока делается снимок. Но это работает только потому, что снимок делает
само приложение. Для чужого захвата — Zoom, OBS — панель остаётся обычным окном. Опять
же, это X11.
По-настоящему невидимый: niri
Самое интересное происходит в Wayland, в niri. Там оверлей — это layer-shell
поверхность с пространством имён invisible-ai, а чтобы её спрятать, хватает
одного правила в конфиге композитора, рядом с моими хоткеями:
layer-rule { // invisible-ai overlay: hidden from screen sharing and screenshots. match namespace="^invisible-ai$" block-out-from "screen-capture"}И всё. Теперь niri вырезает этот слой из любого захвата — Zoom, OBS, Discord, grim,
кто бы ни был на том конце. В том числе из скриншотов самого invisible-ai, которые оно
делает через grim по протоколу wlr-screencopy: модель не видит панель даже в
полноэкранном режиме, и ничего не нужно гасить или мигать.
Ну, почти. Стоковый niri рисует на месте вырезанной поверхности чёрный
прямоугольник — так же, как KWin. Зрители видят непрозрачную плашку поверх
рабочего стола, и для моих целей это хуже, чем ничего: она подсвечивает ровно то, что
хотелось спрятать. macOS и Windows показывают то, что под окном, будто его нет. Мне
хотелось так же, и для этого понадобился небольшой патч композитора —
niri-invisible-layers.patch,
который накладывается на сборку niri в моих dotfiles:
if target.should_block_out(self.rules.block_out_from) { // …draw the solid-color block-out buffer… rv.normal.push(elem.into()); // Draw nothing instead of a black rectangle, so screencasts and screenshots // show whatever is underneath, as if the surface weren't there. return rv;}Вместо того чтобы закрашивать поверхность чёрным, её просто не рисуют — и в захвате виден рабочий стол под ней, ровно как в macOS и Windows. Держать патч поверх своего композитора — осознанный компромисс: десяток строк поверх стокового niri, которые пересобирает моя конфигурация NixOS, в обмен на оверлей, которого на той стороне созвона действительно не видно.
Почему чёрный и что происходит в апстриме
Чёрная плашка — не случайность. Документация niri предлагает block-out-from для
менеджеров паролей и мессенджеров, и там видимый прямоугольник, пожалуй, и есть смысл:
вы видите на превью, что что-то скрыто. Поэтому мой патч хорош для моей машины, но
отправлять его в апстрим в таком виде нельзя — он молча меняет поведение существующей
опции для всех, кто на эту плашку полагается.
Я далеко не первый, кому нужно другое поведение. В Discussions niri (там живут
запросы на фичи) за этот год та же просьба звучала трижды:
#3596 (чёрная плашка подсказывает,
что именно скрыто — «это уведомление», — и на собеседовании выглядит подозрительно),
#4339 (не рисовать layer-shell
поверхности, то есть ровно случай этого оверлея) и
#4564 (окно AI-ассистента, «как
WDA_EXCLUDEFROMCAPTURE»). Мейнтейнер не отказывает: он называет это «обсуждением о
том, чтобы block-out-from умел не рисовать окна», хочет «сначала что-то простое» и
посмотреть, как это устроено в других системах.
И код уже есть: PR #3685 добавляет
отдельное правило hide-from для окон и слоёв, которое просто не включает их в
захват, а block-out-from не трогает. Именно такой дизайн и может быть принят. PR
открыт с марта, ревью у него не было, и теперь его нужно ребейзить, так что я описал
свой случай и там, и в #4564 — вместе с ответом на «как это сделано в других
системах»: и Windows, и macOS окно не зачерняют, а не включают в захват.
Оговорки, потому что «невидимость» — в кавычках
- Это правило композитора, а не функция приложения. invisible-ai не может спрятать себя сам — правило слоя нужно добавить вручную. Без него оверлей виден, как любое другое окно.
- В X11 это по-прежнему невозможно. На моих машинах с i3 это хорошая AI-панель, но не приватная. Если невидимость обязательна — нужен niri, а не X11.
- Патч — моя личная ноша, пока что. Стоковый niri даёт всё, кроме «стол под окном
вместо чёрной плашки», и этот патч мне придётся переносить с каждым обновлением
niri, пока не смержат что-то вроде
hide-fromиз #3685. Тогда это будет одно слово в правиле слоя и никакого патча. - Честно предупреждать, а не обещать. Следующий шаг — определять композитор и писать прямо в панели: «скрыт от захвата» или «виден: у вас X11/GNOME». Оверлей, который не может быть уверен, не должен делать вид, что уверен.
Что дальше
В планах: нативный провайдер Anthropic API; бэкенды для macOS (NSWindow.sharingType)
и Windows (WDA_EXCLUDEFROMCAPTURE), чтобы на всех платформах была одна и та же
гарантия; и функции помощника на созвонах — подсказки по живой расшифровке и
саммари после звонка.
Если вам тоже не хватало такого примитива в Linux — честно говоря, ради менеджера паролей, — коротко: он есть, живёт он в композиторе, и сегодня это niri (или KDE 6.6+), а не X11. Сделайте оверлей layer-shell поверхностью, добавьте правило и не верьте приложениям, которые обещают то же самое везде.