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
@@ -31,14 +31,14 @@ void main() {
expect(state.agcEnabled, isTrue);
});
test('normalizes desktop VAD backend to Silero', () {
test('desktop VAD normalization keeps explicit WebRTC selections', () {
expect(
normalizedVadBackend(
rust.BridgeVadBackend.webrtcVad,
isWindows: true,
isLinux: false,
),
rust.BridgeVadBackend.sileroOnnx,
rust.BridgeVadBackend.webrtcVad,
);
expect(
normalizedVadBackend(
@@ -46,6 +46,14 @@ void main() {
isWindows: false,
isLinux: true,
),
rust.BridgeVadBackend.webrtcVad,
);
expect(
normalizedVadBackend(
rust.BridgeVadBackend.energyDebug,
isWindows: true,
isLinux: false,
),
rust.BridgeVadBackend.sileroOnnx,
);
});
@@ -103,6 +111,7 @@ void main() {
test('builds Windows/Linux software WebRTC APM config consistently', () {
final state = AudioProcessingConfigState.fromConfig(baseConfig)
..vadBackend = rust.BridgeVadBackend.webrtcVad
..nsEnabled = true
..aecEnabled = false
..agcEnabled = true;
@@ -127,7 +136,7 @@ void main() {
for (final config in [windowsConfig, linuxConfig]) {
expect(config.processingBackend, rust.BridgeAudioBackend.webrtcApm);
expect(config.vadBackend, rust.BridgeVadBackend.sileroOnnx);
expect(config.vadBackend, rust.BridgeVadBackend.webrtcVad);
expect(config.aec, rust.BridgeEffectOwner.off);
expect(config.ns, rust.BridgeEffectOwner.webrtcApm);
expect(config.agc, rust.BridgeEffectOwner.webrtcApm);
@@ -0,0 +1,41 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:chanora_flutter/widgets/input_dialogs.dart';
void main() {
test('browser back and forward logical keys map to mouse side bindings', () {
expect(
pttMouseSideButtonPlatformKeyForLogicalKey(
LogicalKeyboardKey.browserBack,
),
'mouse-side-button:8',
);
expect(
pttMouseSideButtonPlatformKeyForLogicalKey(LogicalKeyboardKey.goBack),
'mouse-side-button:8',
);
expect(
pttMouseSideButtonPlatformKeyForLogicalKey(
LogicalKeyboardKey.browserForward,
),
'mouse-side-button:16',
);
});
test('pointer button bitmasks map to mouse side bindings', () {
expect(
pttMouseSideButtonPlatformKeyForButtons(0x08),
'mouse-side-button:8',
);
expect(
pttMouseSideButtonPlatformKeyForButtons(0x10),
'mouse-side-button:16',
);
expect(
pttMouseSideButtonPlatformKeyForButtons(0x18),
'mouse-side-button:8',
);
expect(pttMouseSideButtonPlatformKeyForButtons(0x00), isNull);
});
}
@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:chanora_flutter/services/startup_dependency_check.dart';
import 'package:chanora_flutter/widgets/startup_dependency_screen.dart';
void main() {
testWidgets('startup gate shows install-help screen and can continue', (
tester,
) async {
tester.view.physicalSize = const Size(1200, 1800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final result = StartupDependencyCheckResult(
platformLabel: 'Fedora',
issues: const [
StartupDependencyIssue(
id: 'linux-sdl2-runtime',
title: 'SDL2 runtime is missing',
summary: 'Audio playback needs SDL2.',
details: ['Install SDL2 and recheck.'],
severity: StartupDependencySeverity.required,
installHints: [
StartupInstallHint(
label: 'Release downloads',
command: 'https://github.com/microsoft/onnxruntime/releases',
),
StartupInstallHint(
label: 'Fedora',
command: 'sudo dnf install SDL2',
),
],
),
],
);
final loggedResults = <StartupDependencyCheckResult>[];
await tester.pumpWidget(
MaterialApp(
home: StartupDependencyGate(
checker: () async => result,
logger: (value) async {
loggedResults.add(value);
},
child: const Scaffold(body: Text('ready')),
),
),
);
await tester.pumpAndSettle();
expect(find.text('Finish Linux setup'), findsOneWidget);
expect(find.text('SDL2 runtime is missing'), findsOneWidget);
expect(find.text('Continue with limited mode'), findsOneWidget);
expect(find.text('Open'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget);
expect(loggedResults, [result]);
await tester.ensureVisible(find.text('Continue with limited mode'));
await tester.tap(find.text('Continue with limited mode'));
await tester.pumpAndSettle();
expect(find.text('ready'), findsOneWidget);
});
}
@@ -24,8 +24,9 @@ void main() {
]);
});
test('desktop VAD segments expose Silero as the primary backend', () {
test('desktop VAD segments expose both Silero and WebRTC', () {
expect(desktopVadBackendSegments.map((s) => s.value), [
rust.BridgeVadBackend.webrtcVad,
rust.BridgeVadBackend.sileroOnnx,
]);
});