
Одна CSS-змінна, п'ять раундів рев'ю та ланцюжок інструментів Swift, що збрехав
GeekBye v2.0.7 зробив увесь оверлей регульовано напівпрозорим — що звучить як однорядкова зміна CSS і абсолютно нею не було. Справжня історія — про те, що виловило код-рев'ю: дві поверхні, які відмовлялися згасати, панель запису, що виглядала неправильно за тієї самої прозорості, і скомпільований бінарник, який стрибнув на 672 байти, бо наша власна документація підказала рецензенту неправильну версію Swift.
«Зробіть оверлей напівпрозорим» — це той тип задачі, що звучить як повзунок і CSS-властивість opacity. GeekBye v2.0.7 випустив рівно цей результат для користувача — єдиний регульований «світлий режим», що дозволяє бачити крізь панелі асистента те, що за ними. Але цікава частина цього релізу — не сама функція. Це п'ять раундів рев'ю, які знадобилися, щоб зробити її правильно, і фронтова історія про скомпільований бінарник, що змінив розмір, бо наша власна документація збрехала рецензентові.
Правильний спосіб зробити цілий інтерфейс напівпрозорим
Наївний варіант — розсипати значення прозорості по кожному компоненту-панелі й вважати справу зробленою. Це пастка супроводу: десяток зашитих значень, які розповзаються тієї ж миті, щойно хтось торкнеться одного.
Натомість v2.0.7 використовує одну спільну CSS-утиліту й токен для кожної поверхні. Є єдиний клас overlay-surface, який обчислює фон панелі з двох користувацьких властивостей: базової прозорості, яку кожна поверхня оголошує для себе (панель керування, чат, бульбашки транскрипту, блоки коду тощо — кожна обирає свою власну), і глобального множника альфи, що живе в корені оверлея й представляє налаштування повзунка користувача. Кожна поверхня читає той самий множник; кожна зберігає власний характер. Критично: масштабується лише фон панелі — текст, іконки й backdrop-blur лишаються повністю відмальованими, тож «напівпрозорий» ніколи не означає «нечитабельний».
Два рішення в цьому дизайні варто виокремити, бо саме вони — різниця між функцією, що допомагає, і тією, що породжує звернення до підтримки:
- За замовчуванням оверлей повністю непрозорий. Налаштування прозорості постачається на рівні 100% — тобто нічого не робить. Ніхто не побачив, як його оверлей раптом став прозорим після оновлення; прозорість там, коли ви до неї потягнетеся, і невидима доти.
- Налаштування валідується захисно. Є одне джерело правди для мінімуму, максимуму, значення за замовчуванням і кроку, а також нормалізатор, який захищає порожній/відсутній випадок. Це важливо через одну підступність JavaScript:
Number(null)дорівнює0, тож наївно приведене відсутнє налаштування миттєво перемкнуло б оверлей у найпрозоріший стан замість стану за замовчуванням. Нормалізатор ловить це, перш ніж воно станеться.
Потягніть повзунок — і весь оверлей перетонується наживо, посеред запису, бо кожна поверхня підписана на те саме налаштування. Без перезапуску, без перезавантаження.
Що виловило рев'ю (і чого тест на одному моніторі ніколи б не виловив)
Ось де «одна CSS-змінна» перестала бути простою. Три баги виринули на рев'ю, і два з них — той самий урок у різних масках.
Панель, яка не хотіла згасати. Оверлей клавіатурних скорочень уперто залишався щільним, поки все інше ставало напівпрозорим. Причина: він відмальовується поза піддеревом DOM, що несе змінну прозорості, тож ніколи не успадкував значення, що каскадувало вниз від кореня оверлея. Користувацькі CSS-властивості успадковуються через дерево DOM — і якщо вузол насправді не є нащадком того місця, де ви встановили змінну, він мовчки не отримує нічого. Виправлення — встановити змінну локально й на цій панелі теж.
Підсвітка при наведенні, яка зникла. Кнопки Fn/Assist втратили своє блакитне сяйво при наведенні. Утиліта прозорості встановлювала фон із !important (вона мусить перемагати типові значення поверхонь), і це переїхало фонову підсвітку hover: на тому самому елементі. Колізія специфічності. Виправлення — зробити підсвітку при наведенні так само наполегливою, щоб вона могла відвоювати перемогу при наведенні.
Обидва ці випадки — та сама глибинна істина: каскад CSS-змінної — це справжня межа коректності, а не деталь стилізації. Успадкування й специфічність вирішують, чи ваше значення взагалі досягне елемента, і жодне з них не проявиться на скріншоті, поки не стане помилковим.
Однакова прозорість, різний вигляд. Найтонший: панель запису й панель очікування були виставлені на ідентичну базову прозорість — і все одно виглядали помітно по-різному. Чому? Пігулка запису використовувала важче backdrop-blur, а сильне розмиття над темним фоном усереднюється в бік темнішого результату — тож однакова альфа дала неоднаковий вигляд. У неї також була ледь помітна межа там, де панель очікування мала яскраву кайму градієнтного кільця. Виправлення узгодило рівень розмиття й кільце. Урок закарбувався: однакова альфа — це не однаковий вигляд, щойно в гру входить розмиття. Прозорість — це система взаємодіючих ефектів, а не одне-єдине число.
Бінарник, що змінив розмір, бо документація збрехала
Тепер фронтова історія, і це мій улюблений жанр — той, де інструментарій і документація не збігаються, а сумлінний рецензент потрапляє під перехресний вогонь.
GeekBye постачає деякі попередньо скомпільовані бінарники Swift (нативні помічники для таких речей, як виявлення зустрічей). Оскільки скомпільований бінарник закомічено в репозиторій, кожен розробник мусить компілювати його з точно тією самою версією Swift — різні ланцюжки інструментів дають різні байти з ідентичного вихідного коду, а це означало б фантомний git-конфлікт щоразу, коли хтось перезбирає. Тож є скрипт, який застосовує одну конкретну версію: Swift 6.3.x, перевіряється під час збірки.
Під час рев'ю цього релізу рецензент зауважив, що бінарник слід перезібрати відповідно до «мандатованого репозиторієм ланцюжка інструментів». Розробник зробив саме це — і розмір бінарника змінився з 89,184 байтів на 88,512. Повідомлення коміту навіть казало, що це «відновлення байт-стабільного виводу» шляхом перезбирання з Swift 6.2.1, «мандатованим репозиторієм ланцюжком інструментів».
От тільки 6.2.1 була неправильною версією. Скрипт-регулятор збірки вимагав 6.3.x. Проблема була в тому, що текстова документація — README й посібник для контриб'юторів — усе ще казала 6.2.1 у кількох місцях. Тож сумлінний рецензент і сумлінний розробник, обидва прагнучи зробити правильно, пішли за документацією прямісінько до коміту саме того артефакту, який скрипт-регулятор існує, щоб відкидати. Бінарник 6.2.1 у репозиторії 6.3.x — це точно той дрейф, що зламав би чисту перезбірку кожному іншому розробнику.
Наступний коміт це виправив — і виправлення тут головне. Він не просто перезібрав бінарник назад до форми 6.3.x (розмір відновлено до 89,184, рівно туди, де все починалося). Він також виправив застарілі згадки «6.2.1» у документації, щоб пастку не можна було знову захряснути. Бінарник стрибнув на 672 байти й назад за два коміти, а справжній баг ніколи не був у бінарнику. Він був у тому, що два джерела правди — скрипт-регулятор і письмова документація — не збігалися, а людина довірилася не тому.
Три речі, яких навчив цей реліз
- Каскад CSS-змінної — це межа коректності. Два з трьох багів рев'ю зводилися до змінної, яка не успадкувалася, і
!important, який збив стан наведення. Якщо значення мусить досягти елемента через успадкування або перемогти через специфічність, цей шлях — логіка; перевіряйте його як логіку, а не як стилізацію. - Однакові параметри можуть давати неоднакові результати. Панель запису й панель очікування ділили прозорість і все одно виглядали по-різному, бо інше розмиття змінило результат. Коли ви уніфікуєте візуальну систему на одному числі, перевірте, що інші змінні навколо кожної поверхні не змінюють тихцем того, що це число означає.
- Коли документація й регулятор не збігаються, виправляйте розбіжність — а не лише симптом. Баг був не в поганому бінарнику; він був у документації, яка назвала неправильний ланцюжок інструментів, і скрипті-регуляторі, який казав інакше. Перезбирання бінарника лікує симптом. Узгодження кожного джерела правди — ось справжнє виправлення, бо наступний рецензент теж довіриться документації.
GeekBye v2.0.7 випустив однорідну прозорість — усі граничні випадки переглянуто, а документацію ланцюжка інструментів поставлено на місце. Це останній із релізів «тихого ремесла» перед спринтом надійності, що настав опісля. Про фундамент під ним див. чого насправді коштує друга версія (v2.0.0); про споріднений реліз шліфування — спокійне програмне забезпечення: виправлення мерехтіння й чип режиму відповідей (v2.0.3 + v2.0.5); а про те, як оверлей лишається прихованим під час дзвінка — як залишатися невидимим під час демонстрації екрана.