Назад до девблогу Нотатки до випуску · iPhone та iPad

SharkTTY 1.8.1: тепер він слухає

· Safafish

Нотатка в App Store до 1.8.1 — це чотири рядки про віртуальну мишу. За нею стоїть збірка, у яку вкладено дев’ять днів роботи і яка стала найбільшим окремим стрибком застосунку для iPhone та iPad цього року: голосове введення з необов’язковим шаром AI, клавіатура, перебудована навколо справжніх даних про розкладки, клієнт третього термінального протоколу, посилання для доступу до Mac і формат синхронізації, розрахований на те, щоб пережити власні майбутні версії. Це довга версія.

Тепер він слухає

Набирати команду оболонки на телефоні — найгірша частина роботи з терміналом на телефоні. Тож у 1.8.1 з’явилося голосове введення, зібране етапами приблизно за тиждень.

Перший етап — та частина, що важить найбільше й не коштує нічого: утримуйте мікрофон, говоріть, і розпізнавання виконається на пристрої. Безкоштовно, офлайн, і нічого не покидає телефон. Почуте потрапляє на панель голосового введення як редагований текст, і ви вставляєте його в термінал тоді, коли він виглядає правильно, — не раніше.

Другий етап — про ті випадки, коли він виглядає неправильно. Записи зберігаються фрагментами, тож можна підрізати один із них і розпізнати заново саме цю частину, а не повторювати все речення, а історія тримає попередні записи для прослуховування й ще однієї спроби. На iPad панель голосового введення плаває й масштабується так само, як прозора клавіатура, бо на великому екрані нерухома панель не в тому місці — це вже окрема проблема.

І, якщо хочете, він думає

Розпізнавання дає вам слова. Команді потрібно більше, ніж слова, — їй потрібні прапорці в правильному порядку й жодного пробілу там, де його бути не повинно. Шар AI у 1.8.1 цілком необов’язковий і цілком відокремлений від розпізнавання.

Підключіть власного постачальника: підтримуються формати API OpenAI, Claude, Gemini, Kimi і GLM, а ви задаєте модель для діалогу і, якщо вона є в постачальника, модель транскрибування. Claude працює лише в режимі діалогу й не має аудіо-API, тож записи там повертаються до розпізнавання на пристрої, а не падають з помилкою. Ключі постачальників живуть у Keychain цього пристрою й свідомо виключені із синхронізації.

З під’єднаним постачальником режими підказок перетворюють сказане на чистий текст або на команду, яку можна запустити, а параметр додавання контексту термінала дає моделі побачити, що насправді на екрані, перш ніж відповісти. Є також панель діалогу, тож запитання, яке ви інакше поставили б у вкладці браузера, можна поставити поруч із сесією.

Для тих, хто волів би не морочитися з ключем API, після входу доступні офіційні хмарні моделі; за них платять балами, купленими через вбудовану покупку й спільними для всіх ваших пристроїв. Розпізнавання на пристрої в будь-якому разі лишається безкоштовним, і саме воно стоїть за замовчуванням.

Клавіатура зі справжніми розкладками

Плавуча клавіатура аж до 1.8 була однією жорстко зашитою американською розкладкою. 1.8.1 зробив її керованою даними й привіз шість: американську англійську, британську англійську, японську (JIS), французьку (AZERTY), німецьку (QWERTZ) та іспанську, з перемиканням у налаштуваннях разом із вибором панелі.

Розкладки, керовані даними, потребують тесту, якого не потребують жорстко зашиті, бо розкладка тепер — це таблиця, яку хтось набрав. Ми додали тест досяжності символів: чи для кожної розкладки кожен символ, якого ви очікуєте, справді досяжний якоюсь комбінацією клавіш? Він одразу знайшов дві справжні вади: в AZERTY взагалі не було *, а розкладка JIS прив’язувала fn не до тієї клавіші, тож ряд F збивався. Жодної з них не спіймав би той, хто тестує англійською.

Над клавішами тепер є смужка підказок, яку живить механізм підказки слів, налаштований на контекст термінала й поширені команди, а мікрофон переїхав у верхню панель із двома поведінками: торкання перемикає на панель голосового введення, утримання дає говорити просто в смужку підказок. Цей жест утримання виявився складнішим, ніж здається: очевидний API довгого натискання в SwiftUI не вміє сказати, коли палець піднявся, про що ми окремо написали в дописі про кнопки з утриманням для запису.

Третій шлях усередину: Eternal Terminal

Mosh — це те, що тримає сесію живою в залізничному тунелі, і працює він через UDP. Чимало корпоративних і готельних мереж пропускають лише TCP, і це лишає таких користувачів зі звичайним SSH і без жодного збереження сесії. Eternal Terminal розв’язує ту саму задачу через TCP, і багато хто вже тримає etserver. Ми писали про цей клієнт тижнем раніше; 1.8.1 — збірка, у яку він приїхав.

Він з’явився так, як і має з’являтися експериментальний транспорт: спершу ізольований крейт на Rust, від якого не могло залежати нічого інше; потім підведення через FFI до застосунку на Swift за прапорцем; а коли він запрацював — справжній інтерфейс у редакторі хоста, перемикач у налаштуваннях, повідомлення про помилки, які називають справжню причину, і переклади. Далі було два кола рецензування шляхів відмови — саме там мережевий клієнт найімовірніше тихо помиляється.

Типово він лишається вимкненим. Увімкніть транспорт ET у налаштуваннях — і він з’явиться як третій метод з’єднання поруч із SSH і Mosh. Одне, що варто врахувати заздалегідь: як і UDP у Mosh, з’єднання даних ET іде напряму з пристрою до порту etserver і не пересилається через проміжний хост.

Конфігурація, що переживає власне майбутнє

У цей період агент на Mac обзавівся власним списком хостів і терміналом, а це означало, що телефон і Mac мали домовитися про те, чим є хост. 1.8.1 приніс спільний пакет із канонічною схемою хостів і скриптів, який використовують обидва, а разом із ним клієнт синхронізації конфігурації й координатор, що ним керує.

Спільна схема між двома застосунками, які оновлюються незалежно, створює конкретну небезпеку, і ми в неї втрапили: старіший клієнт декодує значення, про яке ніколи не чув, а потім записує весь запис назад і нищить його. Виправлення — enum, які зберігають своє сире значення, торбина для невідомих полів і тести, що закріплюють контракт, — описане в дописі «Прямосумісний Codable». У цій збірці цей патерн застосували спершу до хостів, а потім до всіх решти синхронізованих моделей, включно з невідомими полями окремих записів у збережених робочих столах.

Що синхронізується, а що ні, лишилося незмінним і чітко проговореним: хости, скрипти, під’єднані сховища, шаблони та налаштування синхронізуються на сервер під вашим обліковим записом. Паролі, приватні ключі SSH та їхні парольні фрази ніколи не покидають пристрій — вони живуть в iOS Keychain, за бажання мандруючи наскрізно через iCloud Keychain, і на сервері немає нічого, що можна було б скомпрометувати.

Поділитися своїм Mac з кимось іншим

1.8.1 додав бік глядача для посилань доступу: список «Спільні зі мною», підтримку транспорту для підключення через спільний доступ, а не через рядок з’єднання, і обробку сесій, які потребують схвалення власника. Помилки тут навмисно конкретні: прострочене посилання, відкликане посилання, зайняті всі одночасні місця, обліковий запис, якого немає в списку дозволених, і підключення поза заброньованим вікном — кожне з них каже, що саме сталося, бо «не вдалося підключитися» нічим не допомагає, коли виправлення залежить від причини.

Тут же: від’єднання віддаленого робочого стола зберігає його вкладку, як це завжди робило з’єднання SSH, тож ви перепідключаєтеся з накладки, а не налаштовуєте все спочатку.

Віртуальна миша отримує своє місце

Це та частина, яку описує нотатка в App Store. Кнопки миші можна було розділяти й розставляти, що було гнучко й означало, що вони ніколи не опинялися саме там, де ваша рука. Тепер вони швартуються: одна панель уздовж нижнього краю екрана, яка ковзає ліворуч чи праворуч туди, де ви тримаєте пристрій, і піднімається над екранною клавіатурою, щойно та відкривається.

Швартування також виправило найгіршу звичку лупи. У вільному плаванні їй доводилося уникати країв екрана, а отже вона рухалася тоді, коли ви цього не чекали, і збільшений вигляд не збігався з курсором. Пришвартована, вона стоїть просто над курсором і лишається там. Два супутні виправлення: якщо провести пальцем за край екрана, курсор більше не стрибає й кнопка не залишається натиснутою — скасований дотик тепер обробляється саме як скасований дотик, — а ⌘, ⌥ і ⌃ більше не лишаються часом натиснутими, перетворюючи наступне натискання клавіші на несподівану комбінацію.

Сигнал, який чути з іншого застосунку

Коли термінал подає сигнал, а ця сесія не та, на яку ви дивитеся, iOS тепер показує сповіщення. Додайте printf '\a' у кінець довгого збирання — і телефон скаже вам, коли воно завершиться. Сигнали від сесії, яку ви й так дивитеся, лишаються беззвучними, і в цьому весь фокус.

Прибирання, про яке варто згадати

  • Повний аудит локалізації наприкінці роботи над клавіатурою: перевірено 639 ключів, дублікатів не лишилося жодного.
  • Два виправлення коректності за результатами рецензування: стан гонки в декодері та паролі в Keychain, які могли осиротіти після зникнення хоста.
  • Аудіошлях Opus із 1.8 завершився саме тут — буферизований кодувальник, що виробляє кадри по 20 ms із довільного PCM, відтворення на боці глядача та виправлення читання перемеженого PCM зі списку аудіобуферів.
  • Тестові цілі знизили до iOS 17.0, щоб вони збігалися із самим застосунком, тож тести виконуються всюди, де працює застосунок.

Усе це безкоштовне. Разом із розпізнаванням голосу на пристрої.

Читати цю статтю: English · 中文 · 日本語 · Español · Français · 한국어 · Українська

Більше про сам застосунок — у посібнику SharkTTY; короткі нотатки для користувачів — у розділі Що нового, а найшвидший спосіб зв’язатися з нами — дошка відгуків. Повернутися до девблогу.