| `microsoftLoginSucceeded` | сообщение. Выбор в списке профилей не трогает: новый профиль уже выбран тем, кто его создал, а повторный вход мог быть не для последнего профиля |
| `microsoftLoginFailed` | красное сообщение на 8 секунд |
| `microsoftReloginRequired` | сразу `startMicrosoftLogin()` для этого профиля |
| `gameOutput` | `console.log` |
| `seasonalInstallFinished` | сообщение «сборка установлена, можно играть» |
| `javaRuntimeInstalled` | обновляет строку Java в настройках |
Кнопка запуска выключена, пока `busy` или `gameRunning`. Кнопка активной сборки выключена во время
игры и смены сборки.
## Профили
Выпадающий список профилей переделан целиком. Наверху всплывающей панели закреплена кнопка
«+ Добавить профиль», под ней список. У каждой строки при наведении появляются карандаш (открывает
редактирование через `openFor()`) и корзина (`removeProfile()`).
Типы входа во всех списках идут в одном порядке: 0 — `offline`, 1 — `elyby`, 2 — `microsoft`.
Модель берётся из `Loc.t.profile.authTypes`, и порядок в каталоге важен, потому что код сравнивает
`currentIndex`с 1 и 2. Без WebEngine третий пункт отрезается через `.slice(0, 2)`. В окне
редактирования он остаётся, если профиль уже лицензионный: иначе в сборке без WebEngine такой
профиль молча стал бы офлайновым.
**Новый профиль.**`addProfile()`, новый профиль выбирается в списке. Если тип «Microsoft», сразу
начинается вход, потому что без него такой профиль бесполезен. Скрытые поля при сохранении не
читаются: в них мог остаться текст, набранный до смены типа.
**Редактирование.** Дополнительные свойства диалога: `editIndex`, `msProfile`, `msLinked` (есть ли
сессия Microsoft), `msName` (ник из аккаунта). Ник показывается отдельным полем только для чтения,
а не подставляется в поле логина. Иначе привязка оборвалась бы при первом же вводе в это поле у
обычного профиля. Кнопка входа сначала сохраняет профиль с типом `microsoft` через
`updateProfile()` и только потом вызывает `startMicrosoftLogin()`. Тип могли переключить только
что, и без этого токены достались бы профилю другого типа.
## Двухфакторная аутентификация
Диалог показывает имя профиля (`profileName`) и поле для кода. «Подтвердить» вызывает
`submitTwoFactorCode()`, «Отмена» — `cancelPendingLaunch()`. Если отмену не вызвать, запуск так и
повиснет.
## Настройки
`settingsDialog.load()` читает `settings()` и `detectedJava()`, раскладывает значения по полям и
открывает диалог. «Сохранить» собирает всё в одну карту и отдаёт в `updateSettings()`.
- **Язык** стоит первым, потому что относится ко всему приложению, а не к запуску игры. В модели
переведённые подписи, рядом лежит непереводимый список `codes` (`system`, `ru`, `en`). Язык
применяется по «Сохранить» и сразу, без перезапуска.
- **Папка игры**, а под полем — путь, который получится на самом деле (`resolvedGameDir`). Путь
хранится в свойстве, а не записывается в текст напрямую, иначе подпись не обновилась бы при смене
языка.
- **Java.** Поле открывает [JavaPickerDialog](JavaPickerDialog.md) с текущим выбором и требованием
активной сборки (`requiredJavaMajor()`). Крестик сбрасывает выбор, и тогда Java ищется в системе.
Выбор хранится в `javaRuntimeId` и записывается только по «Сохранить». `javaRuntimeInfo`
обновляется при смене `javaRuntimeId` и по `javaRuntimeInstalled`, потому что
`javaRuntimeInfo()` — обычный вызов, а не привязка. Если сборка выбрана, запуск идёт ею, а путь
из соседнего поля остаётся запасным вариантом.
- Память, размер окна, аргументы JVM, полноэкранный режим.
- Ниже для справки список найденных в системе java.
Окно выбора Java объявлено рядом с диалогом настроек, а не внутри него. Оно шире настроек и