
Сторінка логіна і Є демо
GeekBye v1.7.5 додав скриншот продукту на сторінку логіна й видалив його того ж дня — перезібравши натомість демо зі справжніх компонентів. Плюс: користувач, який виявився швидшим за OCR, і як насправді виглядають 25 стильових commitів за один день.
GeekBye v1.7.5 — тижневий реліз із роздвоєнням особистості: 37 commits, з яких 28 — редизайн сторінки логіна, а 25 із них — мікроправки style(login), і всі вони приземлилися за один день. Це той самий реліз, що полагодив одну з найповчальніших гонок у кодовій базі. Обидві історії варто розповісти, і сходяться вони в одній ідеї: сторінка логіна — перше, що бачить неавторизований користувач, тож нехай вона краще буде продуктом, а не його картинкою.
Скриншот — годин на п'ять
Редизайн почався конвенційно. Перебудувати на дві колонки, вхід ліворуч, продукт праворуч — картинкою. Пізньоранкові commits роблять саме це: об 11:57 у репозиторій приземляється справжній прев'ю-скриншот, PNG на 114 KB, і за шість хвилин його один раз оновлюють.
О 17:04 його вже не було. Commit-розворот замінює статичну картинку 344 рядками справжніх компонентів — панеллю LoginDemo, зібраною з восьми маленьких шматочків, що відтворюють справжній UI оверлея: пілюля запису в навбарі з пульсуючими стовпчиками еквалайзера й кнопкою «стоп», панель чату з вкладками Chat/Transcript, відрендерена відповідь асистента (що пояснює React-івський useEffect — як символічно), швидкі дії "Tell me more / Simplify / Show example" (розкажи більше / спрости / покажи приклад), ряд живих підказок і поле Assist із ⌘⏎.
Будьмо точними зі словом «демо», бо тут легко переобіцяти: у кнопок справжні hover-стани — світіння, шимери, переходи, — але ніщо не підключене до кліку, а поле вводу лише для читання. Це не пісочниця. Це — справжній UI-словник продукту, відрендерений наживо: той самий асет логотипа, ті самі градієнтні рамки й backdrop-blur, той самий імпорт productName, яким користується white-label-система, — тож демо автоматично ребрендується для кожного продукту, що збирається з цієї кодової бази. Скриншот нічого з цього не вміє. Скриншот застаріває в день зйомки, розмивається на неправильному дисплеї й назавжди забрендований тим продуктом, який ти випадково зафіксував. Компоненти чіткі за будь-якого DPI, завжди актуальні й коректні для кожного бренду — бо вони не фотографія дизайн-системи, вони і є дизайн-система.
Видалений PNG, до речі, досі лежить у репозиторії на тезі релізу — осиротілий, ні на що не посилається. Кожна кодова база носить у собі кілька скам'янілостей підходу, що програв.
Як виглядають 25 стильових commitів за один день
Лог commitів того єдиного дня — це блокнот дизайнера, збережений із незвичною чесністю. Співвідношення колонок пішло 40/60, потім 45/55. Лейбл "Works with" (працює з) над логотипами платформ додали опівдні, пересунули й прибрали — він прожив приблизно пів дня. Сам ряд логотипів платформ (Google Meet, Teams, Zoom, Slack, Webex), доданий о 10:59 ранку, був цілком видалений о 22:08 тієї ж ночі. А футер пережив сагу вирівнювання в чотири підходи: один контейнер-обгортка, потім видалення центрувальних класів, потім центрування флексбоксом, потім просто присунути його ближче до низу.
Легко прочитати це як метушню. Ми читаємо це як справжню форму дизайнерської роботи: ти сходишся до рішення, рендерячи, дивлячись і підправляючи — а робити це у 25 маленьких commitах замість одного сквошнутого «redesign login page» означає, що сам пошук виявляється записаним. Коли хтось спитає «а ми хіба не пробували ряд логотипів?», відповідь лежить в історії, з таймстемпами.
Користувач, який був швидшим за OCR
Інженерна історія релізу сталася п'ятьма днями раніше, у ланцюжку з трьох фікс-commitів. Починаючи з v1.5.12 натискання Cmd+H ставить скриншот у чергу з миттєвим зворотним зв'язком: мініатюра з'являється в черзі одразу, а видобування тексту — локальний OCR Apple Vision, приблизно ~1-2 секунди — крутиться у фоні. Подія черги буквально виходить із ocrText: '' і коментарем, що обіцяє: текст заповниться пізніше.
Це хороша оптимізація — і вона викарбувала гонку. Швидкий користувач тисне Cmd+H і одразу надсилає питання. Текстове поле скриншота досі порожнє. Запит іде без вмісту екрана, і модель відповідає — правдиво й до сказу — "I cannot see images" (я не бачу зображень). Наївний шлях не впав із помилкою; він упав правильною відповіддю на неправильне питання — найзаплутаніший спосіб, у який може ламатися софт.
Фікс блокує шлях надсилання на тій асинхронній роботі, яку оптимізація відклала: якщо в будь-якого скриншота в черзі текст OCR досі порожній, запит паркується, а UI показує індикатор "Reading screen..." (читаю екран); useEffect спрацьовує тієї миті, коли текст приземляється, і надсилає запит із повним видобутим вмістом. Дві React-специфічні деталі в дифі варто вкрасти: черга скриншотів дзеркалиться в ref, що синхронізується під час рендеру, бо ланцюжок замикань обробників подій захоплював застарілий стан; а запаркований запит надсилається напряму, а не через загальний хук черги запитів — із тієї ж причини stale closure.
І просто в повідомленні commitа записана чесна зморшка: трасувальні логи, додані, щоб вистежити цю гонку, розкрили другий баг на бекенді — збирання промпту в одному зі шляхів коду цілком губило текст скриншота. Один симптом, два баги, знайдені інструментуванням усього ланцюжка й спостереженням, де текст зникає. Ланцюжок завершився продуктовим рішенням, за дев'ять хвилин після фіксу гонки: Cmd+H ставить скриншоти в чергу для пакетного аналізу, а Cmd+Enter завжди робить свіжий знімок поточного екрана — два різні воркфлоу замість одного двозначного. (Що насправді робить Assist за Cmd+Enter — тема окремого поста: миттєва допомога AI щодо того, на що ти дивишся.)
Маленька кода того ж тижня для читачів розділу про white-label: User-Agent Product/version (platform) отримав фікс для dev-режиму, бо app.getVersion() у незапакованому Electron повертає версію Electron, а не продукту. Рядки ідентичності, як завжди, протікають у сантехніці.
Три речі, яких нас навчив цей реліз
- Рендери продукт, а не фотографуй його. Демо сторінки логіна, зібране зі справжніх компонентів, саме ребрендується, переживає будь-яку щільність екрана й не може застаріти. Підхід зі скриншотом спробували й видалили в межах одного робочого дня — дешевий експеримент, ясний вердикт.
- Кожній асинхронній оптимізації потрібен шлагбаум у точці використання. «Миттєвий зворотний зв'язок із порожнім текстом OCR» був правильним UX-рішенням, але шляху надсилання довелося навчитися чекати на те, що оптимізація відклала. Якщо відкладаєш роботу — знайди кожного споживача її результату й навчи його різниці між «порожньо» та «ще не готово».
- Інструментуй ланцюжок, а не підозрюваного. Трасувальні логи стежили за текстом скриншота від кінця до кінця — від обробника шортката через IPC до API-клієнта — і саме тому одне розслідування закрило два баги, один із яких був по той бік мережі.
Про попередній розділ історії v1 — зустрічі, які знову можна знайти (v1.7.3); а про всю дугу — анатомію доставки програмного забезпечення до досконалості.