Колонка держит ширину
Новая колонка встаёт в ленту, соседи разъезжаются вдоль неё, но не сжимаются: геометрия открытого окна при вставке не меняется — проверено и арифметикой, и экраном.
Локально и без аккаунта. Аккаунта нет, регистрация не нужна: введённое в инструменты остаётся в localStorage браузера и на сервер не уходит.
Свой счётчик считает открытия страниц и дочитывания: уезжает адрес и десятая доля текста. Без cookies и чужих счётчиков, IP не хранится, Do Not Track уважается. Как это проверить
Репозиторий портала не выложен, «открытым кодом» мы его не зовём. Открыто это:
Живёт портал на донатах, платных консультациях и разборах по запросу и покупке Workbench.
Планов делать курсы платными нет.
Выкладка от 16 сентября 2026, срез 95043e5
Что изменилось на портале с вашего прошлого захода. Полный список выкладок — в разделе «Что изменилось» на странице «О нас».
X11 · BSD-2-Clause · ранний
Окна живут на бесконечной горизонтальной ленте колонок, в колонке — стопка окон; вместе они полотно, а экран — окно просмотра по нему. Полотно едет по обеим осям и за свои края не уходит. Модель как у niri, но на X11: Linux, FreeBSD и NetBSD одинаково. Маковская цель есть в дереве и ни разу не запущена — мака в проекте нет.
Двадцать три кадра записанного сеанса, по которым можно пройти теми же клавишами, что стоят в конфиге, и четырнадцать кадров с работающего менеджера — включая пару «с Workbench и без него».
f3e63de под Xvfb 1280×800, каждый после ровно одного нажатия, числа под ним промерены xwininfo. Обход один: три шага 4-l вдоль ленты и девятнадцать 4-j вниз по стопке из двадцати окон; шага, которого в записи нет, проигрыватель не подрисует. cwmrc.
btop получил 67 %, zsh — 33 %. Полный размер
Один сеанс, четыре кадра: едет вьюпорт, а не колонки
Один сеанс с двоичного файла из 83fc87b, Xvfb 1920×1080: шесть окон Alacritty с vim -R, пресет 67 %, ribbongap 8, палитра Digitable Focus. Рамки у колонки с фокусом здесь нет: Alacritty просит нулевую своим ConfigureRequest, а менеджер выполняет просьбу клиента как есть (xevents.c:209-210).
Один сеанс, два кадра: двадцать окон в одной колонке, полотно 1352 при экране 800
Те самые 552 точки, которых до второй оси было не достать. Один сеанс с двоичного файла из f3e63de, Xvfb 1280×800: двадцать окон Alacritty в одной колонке пресетом 100 %, ribbongap 8, ribbonminheight 60 — те же числа, что в таблице вкладки «Как едет лента». Пространства имён у этих двух кадров нет: оболочки в кадре не запущено ни одной, каждое окно печатает одну строку и засыпает.
editors/vim; на снимке ещё прежний плагин из editors/vim-fts, которого в нынешнем выпуске нет. Полный размер
Менеджер собран из 83fc87b, кроме полосы «Как полотно едет вниз» — её два кадра сняты с f3e63de, до которого второй оси не было. Конфигурация — session/config/cwmrc.in с палитрой Digitable Focus Carbon; дисплей Xvfb 1920×1080, у полосы «вниз» 1280×800. Конфигурации приложений взяты из архива Workbench и разложены кодом галереи тем. Сеанс двенадцати кадров идёт в отдельном пространстве имён: /home/dev вместо домашнего каталога, в /etc/passwd только dev, имя машины workbench, свой список процессов.
Тайловый менеджер делит экран между всеми окнами: открыли пятое — четыре предыдущих стали уже. На ленте колонки стоят в ряд и держат свою ширину, а экран ездит по ним.
Новая колонка встаёт в ленту, соседи разъезжаются вдоль неё, но не сжимаются: геометрия открытого окна при вставке не меняется — проверено и арифметикой, и экраном.
Фокус двигает вьюпорт, а не перестраивает ленту. Колонка с фокусом всегда целиком внутри экрана: после фокуса, вставки, закрытия, смены ширины и смены конфигурации мониторов.
Уехавшие за край окна остаются отображёнными по координате вне экрана. Гасить их можно настройкой ribbonhide.
Анимация требует процесса, который владеет кадром, а оконный менеджер X11 им не владеет: окна переезжают за один шаг. Нужны плавные переходы — рядом запускается композитор.
Те же четыре утверждения — в кадрах витрины.
Смещений два — offset вдоль ленты и voffset вниз по полотну (calmwm.h:339-340), — и оба вычитаются из координат окна на экране (ribbon.c:746-747). Вторая ось появилась в f3e63de; сколько точек терялось до неё — в таблице ниже.
ribbon_policy_offset() (ribbon.c:80-104), модель — fts/scroll-offset.fts.
doc/ribbon.ru.md:28-30). Делит, пока делится: когда равная доля упирается в минимальную высоту, побеждает минимум, и полотно становится выше экрана (ribbon.c:192-212). Дальше вторая ось: ribbon_policy_voffset() (ribbon.c:124-127) везёт вьюпорт за окном с фокусом. Единица другая, чем поперёк: стопка бывает выше любого экрана, поэтому вниз вьюпорт держит окно, а не колонку. Модель — fts/stack-offset.fts.
ribbon.c:95-101): вдоль ленты смещение сидит в [0, длина − ширина экрана], вниз — в [0, высота полотна − высота экрана]. «Бесконечная» — про ленту, не про прокрутку.
Где были потерянные точки. Там, где стояла потеря, теперь стоит предел прокрутки, и это одно число. Снято пробой раскладки — тем самым кодом, который исполняет менеджер: cwm -C 'layout-probe layout viewport=1280x800 gap=8 min-height=60 columns=N presets=3 focus=0'.
| Окон в стопке | Высота полотна | Было до второй оси | Стало |
|---|---|---|---|
| 11 | 800 | Стопка совпадала с экраном точка в точку — терять было нечего. | Предел прокрутки 0. Прокручивать нечего, и модель говорит это отдельным правилом, а не молчанием. |
| 12 | 811 | 11 точек последнего окна навсегда под нижней кромкой. | Предел прокрутки 11 — ровно те же 11 точек, и теперь они доезжают до экрана. |
| 20 | 1 352 | 552 точки под кромкой: восемь окон с тринадцатого по двадцатое целиком за экраном. Они стояли на 0, 68, … 1292 всегда — ribbon-focus-down двигал фокус, не двигая вьюпорт (83fc87b, ribbon.c:826-850). |
Предел прокрутки 552. На двадцатом окне полотно стоит на 552, окно занимает 740…800 и видно целиком; подъём по стопке возвращает полотно к 0. |
То же в движении: четыре кадра вдоль ленты и два кадра стопки из двадцати окон на вкладке «В деле».
~/.digitable/digitwm/digitwmrc, если его нет — ~/.cwmrc, и менеджер говорит вслух, который взял.polybar — отдельная программа со своей настройкой, и за пределами Linux она не проверена.Workbench для этого не нужен, и наоборот. Две программы разговаривают только по X11. digitwm бесплатен и работает сам по себе; Workbench работает в любом оконном менеджере, и что в его поставку не попали ни исходники менеджера, ни линковка с ним, проверяет гейт архива.
cwm — оконный менеджер из базовой системы OpenBSD, взятый через переносимую сборку. Маленький, три библиотеки, два десятка лет в базе. Но решило не это.
a343cc9: 7 242 в .c и .h плюс 641 в parse.y.
x11, xft, xrandr. Ни одной добавленной сверх этого.
ribbon.c — 1 542, probe.c — 1 172, маковская цель — 4 443, остальное — обработка событий, конфигурация и измерительные инструменты.
Главная причина в другом: у cwm нет тайлинга, который пришлось бы выдирать. Лента добавляется на чистое место, а не прививается поверх чужого дерева фреймов. Форк сохраняет историю апстрима целиком — 1 146 коммитов.
Числа, которыми живёт лента, не закопаны в C. Десять скалярных решений — смещение вдоль ленты и вниз по полотну, ширина колонки, высота окна в ней, место для нового окна, фокус после закрытия, смещение при смене монитора, длина полосы под чужую панель, её доля и спор двух панелей — записаны моделями FTS на двух поверхностях, русской и английской.
Внутри оконного менеджера FTS не исполняется никогда. Модели работают только в CI; рантайм остаётся на C, yacc и трёх библиотеках X — иначе NetBSD стал бы недостижим.
surfaces.mjs
10 моделей
Русская и английская поверхности дают один канонический документ. Это не перевод: один парсер, одно представление, сверка поле за полем. Моделей ровно столько, сколько политик в ribbon.c: имена ribbon_policy_* сверяются с именами файлов моделей.
conformance.mjs
448 проверок
Векторы идут двумя путями — через сгенерированный TypeScript и через cwm -C "layout-probe …", то есть через тот самый код, что исполняет менеджер. Совпали все.
selftest.mjs
11 мутаций
Харнессу ломают по одной константе в копии моделей и требуют, чтобы он это заметил. Одна из мутаций — поле у верхней кромки полотна — сначала НЕ ловилась: дыра была настоящая, и вектор к ней дописан.
invariants.mjs
320 сценариев
612 вставок, 5 771 окно в конечных состояниях, зерно 20260804. Снято с двоичного файла, а не с модели: ribbon_insert() зовут ту же, что зовёт обработчик MapRequest.
invariants --selfcheck
7 поломок
Смена пресета у существующей колонки, потеря пикселя высоты у существующего окна, колонка с фокусом за краем вьюпорта, колонка мимо сетки, полотно мимо окна с фокусом, полотно ниже своей самой высокой колонки и колонка, совравшая о высоте своей стопки, — каждая замечена.
Харнессы выше отвечают об арифметике; эти три числа — о картинке. Замер: Xvfb 1280×800, двенадцать окон по одному, sh tools/measure-insert.sh -n 12 -w 500, шестнадцать прогонов — 192 вставки, перерисовки нулевые.
| Что мерили | Результат | Что это значит |
|---|---|---|
| Мерцание | 0 за 11 вставок | Ни одно уже открытое окно не перерисовалось, пока открывалось следующее. |
| Задержка вставки | 1–10 мс от XMapWindow |
Собственная доля менеджера — принять MapRequest, выбрать колонку, выдать геометрию, отобразить. Время до появления окна на экране — другое число: от запуска процесса медиана 108 мс, и меряет она fork, exec, подключение к серверу и занятость машины, а не менеджер. |
| Окна за краем | 0 событий UnmapNotify |
Двенадцать окон на вьюпорте в 1 280 точек: большинство за краем, и ни одно не свёрнуто — ровно это и означает ribbonhide no. |
Почему чисел два. На перезамере загрузка машины выросла вдвое (34–49 и 57–94 на восьми ядрах): медиана «от запуска» выросла вдесятеро, с 10 мс до 108, а медиана собственной доли не сдвинулась — 4 мс против 5.
Ноль был не всегда. До 5c4ba49 замер давал полную перерисовку на вставку: новое окно отображалось раньше, чем лента уезжала вьюпортом, и накрывало соседа целиком, а содержимое закрытого окна X выбрасывает. Починка — перенесённый вызов ribbon_sync(); харнесс инвариантов этого увидеть не мог, он смотрит на числа, а не на экран.
Контрольного образца. DGT-WM-01 просила замерить papersway поверх i3 теми же тремя числами; он не снят — ни i3, ни papersway на машине нет. Значит, ни одно число выше ни с чем не сравнено, и «digitwm быстрее» здесь не написано.
Пять продуктов Digitable — не пять инструментов рядом, а один цикл. digitwm занимает в нём последнюю станцию и сам по этому циклу сделан.
--plan сначала печатает, что произойдёт..fts детерминированными инструментами; данные остаются в ~/.digit.
320 сценариев, 612 вставок, 5 771 окно в конечных состояниях — и семь нарочных поломок, которые харнесс обязан заметить.
Чего в этом цикле нет. Он замкнут на одном продукте — на этом: модели раскладки, сверка с живым двоичным файлом и мутации харнесса сняты здесь, а не в остальных четырёх.
Лента есть и проверена — колонки, стопки, вьюпорт по обеим осям, вставка, фокус, пресеты ширины, отдельная лента на каждый выход. Остальное перечислено ниже.
polybar, а менеджер читает заявленную полосу _NET_WM_STRUT_PARTIAL (calmwm.h:548-549, xutil.c:221-222) и снимает её с рабочей области. На живом баре колонки 798 точек становятся 770 при панели в 28. Не проверено: два монитора, панель снизу и по бокам, батарея и сам polybar на FreeBSD и NetBSD — систем обещано три, проверена одна.bootstrap.sh и порт для pkgsrc прогнаны на Debian, остальное вычитано глазами. Маковская цель (macos/, три способа установки, формула brew) не запускалась ни разу: мака в проекте нет.Почему панель чужая — решено числом. Своя стоила бы около 1 550 строк (bar.c у sdorfehs — 1 093, drw.c у dwm — 471) на каждой из трёх систем; место для чужой обошлось в 310 строк, из них 178 не комментарии. Размеры, лицензии и отвергнутые кандидаты — в doc/panel.md.
С голой системы — одной командой; --plan сначала печатает, что произойдёт, и ничего не делает. На маке тот же скрипт собирает маковскую цель, а не X11-сборку.
git clone https://github.com/digitable-lol/digitwm
cd digitwm
sh bootstrap.sh --plan
sh bootstrap.sh
session/verify.sh
Открыть репозиторий
Документация двуязычная — 13 пар, и каждая называет, что в ней замерено, а что нет: doc/ribbon.md (модель ленты), doc/baseline.md (три числа) и doc/macos-install.md (мак).
Граница проведена по коммиту, которым файл добавлен: 137 наших файлов под BSD-2-Clause, 25 из 26 унаследованных от cwm — под ISC, и queue.h под BSD-3-Clause. ISC досталась дереву по наследству, а не была выбрана; смена действует вперёд и ничего ни у кого не отзывает (NOTICE).
papersway (GPL-3.0-or-later), sdorfehs и ratpoison (GPL-2.0-or-later) дают похожий скроллируемый тайлинг. Заимствование оттуда сдвинуло бы digitwm на копилефт и закрыло бы путь патчей обратно в cwm. Идеи наблюдать можно, код — нельзя.
И не потому, что лицензия запрещает: продавать разрешают обе — ISC говорит «with or without fee» прямым текстом, BSD-2-Clause не запрещает и подавно. Запрет — записанное решение: разрешительная лицензия заведена ради pkgsrc и портов, а деньги берутся с архива Workbench.
Гейт архива падает, если в поставку попадают исходники, бинарь или узнаваемый код менеджера, и отдельно проверяет, что в дереве оболочки нет ни исходников digitwm, ни линковки с ним.
Каталог session/ настраивает окружение вокруг менеджера, но ни один из инструментов в репозиторий не кладётся: они ставятся пакетным менеджером системы под своими лицензиями. Своё здесь — конфигурация и скрипты установки.