feat(protobufs): sync to firmware-current and consume workspace package (#1097)

* feat(protobufs): sync to firmware-current and consume workspace package

Sync the vendored .proto sources to firmware-current (v2.7.25+48), regenerate the v2 TS bindings, and consume the workspace @meshtastic/protobufs (workspace:*) in place of the stale JSR 2.7.20 — finishing the monorepo migration (core was already workspace:*).

Includes the one required breaking-change fix: admin nodedb_reset changed int32 to bool, so resetNodes() now sends value: true.

* build(protobufs): vendor generated bindings for workspace consumers

The package is consumed via workspace:* — its exports point at the TS source, which imports ./dist/meshtastic/*_pb.ts — so the generated output must exist at build time. CI builds web/core with no codegen step and the runners have no buf CLI, so the bindings are vendored here (kept gitignored; lint/format skip them). Regenerate with: pnpm --filter @meshtastic/protobufs gen

* fix(protobufs): clean script removes the actual generated output dir

buf writes bindings to packages/ts/dist, but clean was removing a non-existent root dist — so it never cleaned stale output. Addresses Copilot review feedback.

* refactor: move web app packages/web -> apps/web

Aligns the web app with the apps/web layout (matching the Vercel web-test Root Directory and the SDK-migration direction). Pure directory move plus root config: pnpm-workspace (adds apps/*), vitest projects, root tsconfig reference, and the pr/release-web/nightly workflows. vercel.json moved with the app. Build + 36 validation tests green.

* feat: config fields, module pages, key verification, telemetry capture

Incorporates the firmware-current feature work onto the protobuf foundation: new config fields (Display message bubbles; LoRa fem_lna_mode + serial_hal_only; Telemetry air_quality_screen_enabled); 4 new ModuleConfig pages (TrafficManagement, StatusMessage, TAK, RemoteHardware); the manual Key Verification flow (sendKeyVerification + ClientNotificationDialog stages + Verify Key button); live telemetry capture (nodeDB addDeviceMetrics) and admin hardening (toggleMutedNode, graceful PortNum default); plus the sdk-preview ConfigEditor demo and store/config tests. Build + lint + format + 131 tests green.

* chore: drop #1062 (unsaved-change-detection) to match upstream revert

#1062 was merged to main by accident (per @danditomaso) and is being reverted. Reverse-applied its diff here via 3-way so #1097 stays consistent with where main is headed, while keeping the feature changes layered on the same files (deviceStore/changeRegistry). Build + 131 tests + lint + format green.

* fix(nodes): clean up SNR display in node table and map popup

SNR is a ratio measured in dB, not dBm (which is absolute power); the
node table and map popup both mislabeled it and crammed three values
together: '0dBm/50%/50raw'. The trailing '%/raw' pair was the same
heuristic ((snr+10)*5) shown twice — once clamped, once not.

Render SNR in dB rounded to one decimal, color-coded by a 0-100%
signal-quality heuristic (green/yellow/red), with the quality percentage
as a muted secondary. Drop the redundant raw value. Adds unit.db; this
matches the existing SNRTooltip, which already renders dB.
This commit is contained in:
Ben Meadors
2026-06-15 13:23:28 -05:00
committed by GitHub
parent 7a8529b841
commit bbe9a0d5cd
695 changed files with 27038 additions and 786 deletions
@@ -0,0 +1,238 @@
{
"deleteMessages": {
"description": "Это действие очистит всю историю сообщений. Это нельзя отменить. Вы уверены, что хотите продолжить?",
"title": "Очистить все сообщения"
},
"import": {
"description": "Импортируйте набор каналов по URL Meshtastic. <br />Корректные URL Meshtastic начинаются с \"<italic>https://meshtastic.org/e/...</italic>\"",
"error": {
"invalidUrl": "Некорректный URL Meshtastic"
},
"channelPrefix": "Канал ",
"primary": "Первичный ",
"doNotImport": "Не импортировать",
"channelName": "Имя",
"channelSlot": "Слот",
"channelSetUrl": "URL набора каналов/QR-кода",
"useLoraConfig": "Импортировать конфигурацию LoRa",
"presetDescription": "Текущая конфигурация LoRa будет заменена.",
"title": "Импорт каналов"
},
"locationResponse": {
"title": "Местоположение: {{identifier}}",
"altitude": "Высота: ",
"coordinates": "Координаты: ",
"noCoordinates": "Нет координат"
},
"pkiRegenerateDialog": {
"title": "Сгенерировать Pre-Shared Key заново?",
"description": "Вы уверены, что хотите пересоздать Pre-Shared Key?",
"regenerate": "Сгенерировать заново"
},
"addConnection": {
"title": "Добавить подключение",
"description": "Выберите тип подключения и заполните параметры",
"validation": {
"requiresWebBluetooth": "Для этого типа подключения требуется <0>Web Bluetooth</0>. Используйте поддерживаемый браузер, например Chrome или Edge.",
"requiresWebSerial": "Для этого типа подключения требуется <0>Web Serial</0>. Используйте поддерживаемый браузер, например Chrome или Edge.",
"requiresSecureContext": "Этому приложению требуется <0>безопасный контекст</0>. Подключайтесь через HTTPS или localhost.",
"additionallyRequiresSecureContext": "Кроме того, требуется <0>безопасный контекст</0>. Подключайтесь через HTTPS или localhost."
},
"bluetoothConnection": {
"namePlaceholder": "Моя Bluetooth-нода",
"supported": {
"title": "Web Bluetooth поддерживается"
},
"notSupported": {
"title": "Web Bluetooth не поддерживается",
"description": "Ваш браузер или устройство не поддерживает Web Bluetooth"
},
"short": "BT: {{deviceName}}",
"long": "Bluetooth-устройство",
"device": "Устройство",
"selectDevice": "Выберите устройство",
"selected": "Bluetooth-устройство выбрано",
"notSelected": "Устройство не выбрано",
"helperText": "Используется служба Meshtastic Bluetooth для обнаружения."
},
"serialConnection": {
"namePlaceholder": "Моя COM-нода",
"helperText": "Выбор порта даёт разрешение, чтобы приложение могло открыть его для подключения.",
"supported": {
"title": "Web Serial поддерживается"
},
"notSupported": {
"title": "Web Serial не поддерживается",
"description": "Ваш браузер или устройство не поддерживает Web Serial"
},
"portSelected": {
"title": "Порт выбран",
"description": "Разрешения на порт предоставлены."
},
"port": "Порт",
"selectPort": "Выберите порт",
"deviceName": "USB {{vendorId}}:{{productId}}",
"notSelected": "Порт не выбран"
},
"httpConnection": {
"namePlaceholder": "Моя HTTP-нода",
"inputPlaceholder": "192.168.1.10 или meshtastic.local",
"heading": "URL или IP",
"useHttps": "Использовать HTTPS",
"invalidUrl": {
"title": "Неверный URL",
"description": "Введите корректный URL HTTP или HTTPS."
},
"connectionTest": {
"description": "Проверьте подключение перед сохранением, чтобы убедиться, что устройство доступно.",
"button": {
"loading": "Проверка...",
"label": "Проверить подключение"
},
"reachable": "Доступно",
"notReachable": "Недоступно",
"success": {
"title": "Проверка подключения успешна",
"description": "Устройство доступно."
},
"failure": {
"title": "Проверка подключения не удалась",
"description": "Не удалось связаться с устройством. Проверьте URL и попробуйте снова."
}
}
}
},
"nodeDetails": {
"message": "Сообщение",
"requestPosition": "Запросить позицию",
"traceRoute": "Трассировка маршрута",
"airTxUtilization": "Загрузка TX в эфире",
"allRawMetrics": "Все сырые метрики:",
"batteryLevel": "Уровень батареи",
"channelUtilization": "Загрузка канала",
"details": "Детали:",
"deviceMetrics": "Метрики устройства:",
"hardware": "Оборудование: ",
"lastHeard": "Последний раз слышен: ",
"nodeHexPrefix": "HEX ноды: ",
"nodeNumber": "Номер ноды: ",
"position": "Позиция:",
"role": "Роль: ",
"uptime": "Время работы: ",
"voltage": "Напряжение",
"title": "Детали ноды {{identifier}}",
"ignoreNode": "Игнорировать ноду",
"removeNode": "Удалить ноду",
"unignoreNode": "Не игнорировать ноду",
"security": "Безопасность:",
"publicKey": "Публичный ключ: ",
"messageable": "Доступен для сообщений: ",
"KeyManuallyVerifiedTrue": "Публичный ключ вручную подтверждён",
"KeyManuallyVerifiedFalse": "Публичный ключ не подтверждён вручную"
},
"pkiBackup": {
"loseKeysWarning": "Если вы потеряете ключи, потребуется сбросить устройство.",
"secureBackup": "Важно сделать резервную копию публичного и приватного ключей и хранить её безопасно!",
"footer": "=== КОНЕЦ КЛЮЧЕЙ ===",
"header": "=== КЛЮЧИ MESHTASTIC ДЛЯ {{longName}} ({{shortName}}) ===",
"privateKey": "Приватный ключ:",
"publicKey": "Публичный ключ:",
"fileName": "meshtastic_keys_{{longName}}_{{shortName}}.txt",
"title": "Резервное копирование ключей"
},
"pkiBackupReminder": {
"description": "Рекомендуем регулярно делать резервную копию ключевых данных. Хотите сделать копию сейчас?",
"title": "Напоминание о резервной копии",
"remindLaterPrefix": "Напомнить через",
"remindNever": "Никогда не напоминать",
"backupNow": "Сделать копию сейчас"
},
"pkiRegenerate": {
"description": "Вы уверены, что хотите пересоздать пару ключей?",
"title": "Пересоздать пару ключей"
},
"qr": {
"addChannels": "Добавить каналы",
"replaceChannels": "Заменить каналы",
"description": "Текущая конфигурация LoRa также будет передана.",
"sharableUrl": "Ссылка для обмена",
"title": "Сгенерировать QR-код"
},
"reboot": {
"title": "Перезагрузка устройства",
"description": "Перезагрузить сейчас или запланировать перезагрузку подключённой ноды. При желании можно перезагрузить в режим OTA (Over-the-Air).",
"ota": "Перезагрузить в режим OTA",
"enterDelay": "Укажите задержку",
"scheduled": "Перезагрузка запланирована",
"schedule": "Запланировать перезагрузку",
"now": "Перезагрузить сейчас",
"cancel": "Отменить запланированную перезагрузку"
},
"refreshKeys": {
"description": {
"acceptNewKeys": "Это удалит ноду с устройства и запросит новые ключи.",
"keyMismatchReasonSuffix": ". Это связано с тем, что текущий публичный ключ удалённой ноды не совпадает с ранее сохранённым ключом для этой ноды.",
"unableToSendDmPrefix": "Ваша нода не может отправить личное сообщение ноде: "
},
"acceptNewKeys": "Принять новые ключи",
"title": "Несоответствие ключей — {{identifier}}"
},
"removeNode": {
"description": "Вы уверены, что хотите удалить эту ноду?",
"title": "Удалить ноду?"
},
"shutdown": {
"title": "Запланировать выключение",
"description": "Выключить подключённую ноду через X минут."
},
"traceRoute": {
"routeToDestination": "Маршрут к месту назначения:",
"routeBack": "Маршрут обратно:"
},
"tracerouteResponse": {
"title": "Трассировка: {{identifier}}"
},
"unsafeRoles": {
"confirmUnderstanding": "Да, я понимаю, что делаю",
"conjunction": " и запись в блоге о ",
"postamble": " и понимаю последствия изменения роли.",
"preamble": "Я прочитал(а) ",
"choosingRightDeviceRole": "Как выбрать правильную роль устройства",
"deviceRoleDocumentation": "Документация по ролям устройств",
"title": "Вы уверены?"
},
"managedMode": {
"confirmUnderstanding": "Да, я понимаю, что делаю",
"title": "Вы уверены?",
"description": "Включение управляемого режима блокирует клиентские приложения (включая веб-клиент) от записи конфигурации в радио. После включения конфигурации радиомодуля можно менять только через сообщения удалённого администратора. Эта настройка не требуется для удалённого администрирования нод."
},
"clientNotification": {
"title": "Уведомление клиента",
"TraceRoute can only be sent once every 30 seconds": "Трассировку можно отправлять не чаще одного раза в 30 секунд",
"Compromised keys were detected and regenerated.": "Обнаружены скомпрометированные ключи и они были пересозданы."
},
"resetNodeDb": {
"title": "Сброс базы нод",
"description": "Это действие удалит все ноды из базы нод подключённого устройства и очистит всю историю сообщений в клиенте. Это нельзя отменить. Вы уверены, что хотите продолжить?",
"confirm": "Сбросить базу нод",
"failedTitle": "Произошла ошибка при сбросе базы нод. Повторите попытку."
},
"clearAllStores": {
"title": "Очистить всё локальное хранилище",
"description": "Это действие очистит все локально сохранённые данные, включая историю сообщений и базы нод для всех ранее подключённых устройств. После завершения потребуется заново подключиться к ноде. Это нельзя отменить. Вы уверены, что хотите продолжить?",
"confirm": "Очистить всё локальное хранилище",
"failedTitle": "Произошла ошибка при очистке локального хранилища. Повторите попытку."
},
"factoryResetDevice": {
"title": "Сброс устройства к заводским настройкам",
"description": "Это действие выполнит сброс подключённого устройства к заводским настройкам, удалив все конфигурации и данные на устройстве, а также все ноды и сообщения, сохранённые в клиенте. Это нельзя отменить. Вы уверены, что хотите продолжить?",
"confirm": "Сбросить устройство к заводским настройкам",
"failedTitle": "Произошла ошибка при выполнении сброса. Повторите попытку."
},
"factoryResetConfig": {
"title": "Сброс конфигурации к заводским настройкам",
"description": "Это действие выполнит сброс конфигурации на подключённом устройстве, удалив все конфигурации на устройстве. Это нельзя отменить. Вы уверены, что хотите продолжить?",
"confirm": "Сбросить конфигурацию к заводским настройкам",
"failedTitle": "Произошла ошибка при выполнении сброса. Повторите попытку."
}
}