Reduce Linux setup ambiguity and surface desktop input/message failures honestly

Clarify ONNX Runtime guidance with direct-open install hints, restore desktop WebRTC VAD visibility, map mouse side buttons through focused PTT capture/runtime paths, and wait for server acks before showing chat sends as successful.

Constraint: Linux release UX must stay functional when ONNX Runtime is optional and GNOME portal availability varies
Rejected: Keep desktop VAD locked to Silero only | misleads users when ONNX Runtime is skipped
Confidence: medium
Scope-risk: moderate
Directive: Preserve the protocol send-ack wait path for chat so UI success always tracks real server acceptance
Tested: flutter analyze lib/main.dart lib/widgets/chat_views.dart lib/widgets/input_dialogs.dart lib/widgets/startup_dependency_screen.dart; flutter test test/widgets/input_dialogs_test.dart test/widgets/chat_views_test.dart test/services/startup_dependency_check_test.dart test/widgets/startup_dependency_screen_test.dart test/widgets/voice_settings_controls_test.dart test/widgets/audio_processing_config_state_test.dart; cargo test -p chanora_protocol --lib; cargo test -p chanora_audio ptt_backends --lib
Not-tested: Live manual GNOME portal rebind/global PTT on a real desktop session; observer-bot chat against a live server after the sender-name fallback change
This commit is contained in:
Edison Jwa
2026-05-25 11:55:10 +09:00
parent 5c8c6df16b
commit b8df25a195
16 changed files with 1635 additions and 391 deletions
@@ -4,6 +4,9 @@ import 'package:flutter/services.dart';
import '../l10n/generated/app_localizations.dart';
import '../src/rust/api.dart' as rust;
const int pttMouseBackButtonBitmask = 0x08;
const int pttMouseForwardButtonBitmask = 0x10;
/// Translate a [LogicalKeyboardKey] into the platform-neutral label
/// stored by the PTT binding flow.
String? pttDisplayLabelForKey(LogicalKeyboardKey k) {
@@ -46,6 +49,27 @@ String? pttDisplayLabelForKey(LogicalKeyboardKey k) {
return fallback;
}
String? pttMouseSideButtonPlatformKeyForLogicalKey(LogicalKeyboardKey key) {
return switch (key) {
LogicalKeyboardKey.browserBack ||
LogicalKeyboardKey.goBack => 'mouse-side-button:$pttMouseBackButtonBitmask',
LogicalKeyboardKey.browserForward =>
'mouse-side-button:$pttMouseForwardButtonBitmask',
_ => null,
};
}
String? pttMouseSideButtonPlatformKeyForButtons(int buttons) {
if ((buttons & pttMouseBackButtonBitmask) == pttMouseBackButtonBitmask) {
return 'mouse-side-button:$pttMouseBackButtonBitmask';
}
if ((buttons & pttMouseForwardButtonBitmask) ==
pttMouseForwardButtonBitmask) {
return 'mouse-side-button:$pttMouseForwardButtonBitmask';
}
return null;
}
/// Result of a successful PTT binding capture.
class CapturedBinding {
const CapturedBinding({required this.inputClass, required this.platformKey});
@@ -181,6 +205,16 @@ class _PttBindingCaptureDialogState extends State<PttBindingCaptureDialog> {
KeyEventResult _onKeyEvent(FocusNode node, KeyEvent event) {
if (event is! KeyDownEvent) return KeyEventResult.ignored;
final mouseSideButton = pttMouseSideButtonPlatformKeyForLogicalKey(
event.logicalKey,
);
if (mouseSideButton != null) {
setState(() {
_captured = mouseSideButton;
_capturedClass = rust.BridgePttInputClass.mouseSideButton;
});
return KeyEventResult.handled;
}
final label = pttDisplayLabelForKey(event.logicalKey);
if (label == null) return KeyEventResult.ignored;
setState(() {
@@ -190,9 +224,9 @@ class _PttBindingCaptureDialogState extends State<PttBindingCaptureDialog> {
return KeyEventResult.handled;
}
void _captureMouseSideButton(int button) {
void _captureMouseSideButton(String platformKey) {
setState(() {
_captured = 'mouse-side-button:$button';
_captured = platformKey;
_capturedClass = rust.BridgePttInputClass.mouseSideButton;
});
}
@@ -212,10 +246,11 @@ class _PttBindingCaptureDialogState extends State<PttBindingCaptureDialog> {
child: Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (e) {
const int back = 0x08;
const int forward = 0x10;
if (e.buttons == back || e.buttons == forward) {
_captureMouseSideButton(e.buttons);
final platformKey = pttMouseSideButtonPlatformKeyForButtons(
e.buttons,
);
if (platformKey != null) {
_captureMouseSideButton(platformKey);
}
},
child: Column(