feat: integrate chat voice and diagnostics client
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Chanora design tokens for connection, voice, and latency states.
|
||||
// Maps abstract server/voice/network state to Material 3 colour, icon,
|
||||
// and label affordances.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Tokenised visual mapping for a connection state.
|
||||
class ConnectionTokens {
|
||||
final Color color;
|
||||
final Color? background;
|
||||
final IconData icon;
|
||||
final String label;
|
||||
|
||||
const ConnectionTokens({
|
||||
required this.color,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
this.background,
|
||||
});
|
||||
|
||||
static ConnectionTokens disconnected(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.onSurfaceVariant,
|
||||
background: cs.surfaceContainerHighest,
|
||||
icon: Icons.cloud_off,
|
||||
label: 'Disconnected',
|
||||
);
|
||||
|
||||
static ConnectionTokens connecting(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.primary,
|
||||
background: cs.primaryContainer,
|
||||
icon: Icons.sync,
|
||||
label: 'Connecting',
|
||||
);
|
||||
|
||||
static ConnectionTokens synchronizing(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.secondary,
|
||||
background: cs.secondaryContainer,
|
||||
icon: Icons.hourglass_top,
|
||||
label: 'Synchronizing',
|
||||
);
|
||||
|
||||
static ConnectionTokens connected(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.tertiary,
|
||||
background: cs.tertiaryContainer,
|
||||
icon: Icons.cloud_done,
|
||||
label: 'Connected',
|
||||
);
|
||||
|
||||
static ConnectionTokens reconnecting(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.error,
|
||||
background: cs.errorContainer,
|
||||
icon: Icons.restart_alt,
|
||||
label: 'Reconnecting',
|
||||
);
|
||||
|
||||
static ConnectionTokens lost(ColorScheme cs) => ConnectionTokens(
|
||||
color: cs.error,
|
||||
background: cs.errorContainer,
|
||||
icon: Icons.warning_amber,
|
||||
label: 'Connection Lost',
|
||||
);
|
||||
}
|
||||
|
||||
/// Tokenised visual mapping for a voice transmit/mute state.
|
||||
class VoiceTokens {
|
||||
final Color color;
|
||||
final Color? background;
|
||||
final IconData icon;
|
||||
|
||||
const VoiceTokens({
|
||||
required this.color,
|
||||
required this.icon,
|
||||
this.background,
|
||||
});
|
||||
|
||||
static VoiceTokens idle(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.onSurfaceVariant,
|
||||
icon: Icons.mic_none,
|
||||
);
|
||||
|
||||
static VoiceTokens pttHeld(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.primary,
|
||||
background: cs.primaryContainer,
|
||||
icon: Icons.mic,
|
||||
);
|
||||
|
||||
static VoiceTokens voiceActivity(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.tertiary,
|
||||
background: cs.tertiaryContainer,
|
||||
icon: Icons.mic,
|
||||
);
|
||||
|
||||
static VoiceTokens continuousTx(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.primary,
|
||||
background: cs.primaryContainer,
|
||||
icon: Icons.settings_voice,
|
||||
);
|
||||
|
||||
static VoiceTokens muted(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.error,
|
||||
background: cs.errorContainer,
|
||||
icon: Icons.mic_off,
|
||||
);
|
||||
|
||||
static VoiceTokens outputMuted(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.error,
|
||||
icon: Icons.headset_off,
|
||||
);
|
||||
|
||||
static VoiceTokens deafened(ColorScheme cs) => VoiceTokens(
|
||||
color: cs.error,
|
||||
background: cs.errorContainer,
|
||||
icon: Icons.hearing_disabled,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tokenised visual mapping for network latency/quality.
|
||||
class LatencyTokens {
|
||||
final Color color;
|
||||
final String label;
|
||||
|
||||
const LatencyTokens({required this.color, required this.label});
|
||||
|
||||
static LatencyTokens good(ColorScheme cs) => LatencyTokens(
|
||||
color: cs.tertiary,
|
||||
label: 'Good',
|
||||
);
|
||||
|
||||
static LatencyTokens warning(ColorScheme cs) => LatencyTokens(
|
||||
color: cs.error,
|
||||
label: 'Warning',
|
||||
);
|
||||
|
||||
static LatencyTokens unknown(ColorScheme cs) => LatencyTokens(
|
||||
color: cs.outlineVariant,
|
||||
label: '—',
|
||||
);
|
||||
}
|
||||
|
||||
/// Tokens for channel membership status.
|
||||
class ChannelTokens {
|
||||
final IconData? icon;
|
||||
final String? label;
|
||||
|
||||
const ChannelTokens({this.icon, this.label});
|
||||
|
||||
static ChannelTokens inChannel(ColorScheme cs) => ChannelTokens(
|
||||
icon: Icons.headset_mic,
|
||||
label: 'Connected',
|
||||
);
|
||||
|
||||
static ChannelTokens joining(ColorScheme cs) => ChannelTokens(
|
||||
icon: Icons.login,
|
||||
label: 'Joining...',
|
||||
);
|
||||
|
||||
static ChannelTokens notInChannel(ColorScheme cs) => ChannelTokens(
|
||||
icon: null,
|
||||
label: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tokens for PTT capability display.
|
||||
class PttTokens {
|
||||
final String label;
|
||||
final Color? color;
|
||||
|
||||
const PttTokens({required this.label, this.color});
|
||||
|
||||
static PttTokens focused(ColorScheme cs) => PttTokens(
|
||||
label: 'Keyboard PTT',
|
||||
color: cs.tertiary,
|
||||
);
|
||||
|
||||
static PttTokens platformHook(ColorScheme cs) => PttTokens(
|
||||
label: 'Global PTT',
|
||||
color: cs.primary,
|
||||
);
|
||||
|
||||
static PttTokens none(ColorScheme cs) => PttTokens(
|
||||
label: 'No PTT input',
|
||||
color: cs.error,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SRS-115: Unsupported/degraded platform behaviours documentation.
|
||||
// Describes what is available, degraded, or unavailable per platform.
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
/// Describes capability tier for a feature on a platform.
|
||||
enum CapabilityTier { supported, degraded, unavailable }
|
||||
|
||||
/// A single platform capability description.
|
||||
class PlatformCapability {
|
||||
final String feature;
|
||||
final String description;
|
||||
final CapabilityTier tier;
|
||||
|
||||
const PlatformCapability({
|
||||
required this.feature,
|
||||
required this.description,
|
||||
required this.tier,
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the current platform's capability matrix.
|
||||
List<PlatformCapability> currentPlatformCapabilities() {
|
||||
if (kIsWeb) {
|
||||
return [
|
||||
PlatformCapability(
|
||||
feature: 'Audio',
|
||||
description: 'Web audio is not supported in the beta.',
|
||||
tier: CapabilityTier.unavailable,
|
||||
),
|
||||
];
|
||||
}
|
||||
if (Platform.isAndroid) {
|
||||
return _androidCapabilities();
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
return _iosCapabilities();
|
||||
}
|
||||
if (Platform.isMacOS) {
|
||||
return _macosCapabilities();
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return _windowsCapabilities();
|
||||
}
|
||||
if (Platform.isLinux) {
|
||||
return _linuxCapabilities();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
List<PlatformCapability> _androidCapabilities() => [
|
||||
const PlatformCapability(
|
||||
feature: 'Voice capture',
|
||||
description: 'Oboe AAudio backend; Bluetooth SCO supported.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Voice processing',
|
||||
description: 'WebRTC AEC3/NS/AGC2, VAD via Silero ONNX.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Foreground service',
|
||||
description: 'Persistent notification during active connection.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Global PTT hotkey',
|
||||
description: 'Not available on Android.',
|
||||
tier: CapabilityTier.unavailable,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Audio device selection',
|
||||
description: 'System-managed; wired/BT/SCO automatic routing.',
|
||||
tier: CapabilityTier.degraded,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Push-to-talk button',
|
||||
description: 'Media button / volume-key binding supported.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
];
|
||||
|
||||
List<PlatformCapability> _iosCapabilities() => [
|
||||
const PlatformCapability(
|
||||
feature: 'Voice capture',
|
||||
description: 'VoiceProcessingIO AudioUnit (default); Sonora raw mode optional.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Voice processing',
|
||||
description: 'Platform AEC/NS/AGC via VPIO; optional Rust AEC3/NS/AGC2 via Sonora.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Audio route switching',
|
||||
description: 'Handles speaker/earpiece/Bluetooth route changes and interruptions.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Global PTT hotkey',
|
||||
description: 'Not available on iOS.',
|
||||
tier: CapabilityTier.unavailable,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Background audio',
|
||||
description: 'Supported via AVAudioSession background mode.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
];
|
||||
|
||||
List<PlatformCapability> _macosCapabilities() => [
|
||||
const PlatformCapability(
|
||||
feature: 'Voice capture',
|
||||
description: 'cpal device enumeration; VoiceProcessingIO optional.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Voice processing',
|
||||
description: 'WebRTC AEC3/NS/AGC2; VPIO available on macOS.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Global PTT hotkey',
|
||||
description: 'Supported via platform-global key-binding API.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Secure storage',
|
||||
description: 'macOS Keychain.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Audio device selection',
|
||||
description: 'System audio output route picker.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
];
|
||||
|
||||
List<PlatformCapability> _windowsCapabilities() => [
|
||||
const PlatformCapability(
|
||||
feature: 'Voice capture',
|
||||
description: 'WASAPI via cpal.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Voice processing',
|
||||
description: 'WebRTC AEC3/NS/AGC2.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Global PTT hotkey',
|
||||
description: 'Supported via platform-global hotkey binding.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Secure storage',
|
||||
description: 'Windows Credential Manager / DPAPI.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Installer',
|
||||
description: 'MSIX packaging not yet available in beta.',
|
||||
tier: CapabilityTier.unavailable,
|
||||
),
|
||||
];
|
||||
|
||||
List<PlatformCapability> _linuxCapabilities() => [
|
||||
const PlatformCapability(
|
||||
feature: 'Voice capture',
|
||||
description: 'PulseAudio/ALSA via cpal.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Voice processing',
|
||||
description: 'WebRTC AEC3/NS/AGC2; no platform VPIO.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Global PTT hotkey',
|
||||
description: 'Supported via X11/Wayland global key-binding. ',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Secure storage',
|
||||
description: 'Secret Service / libsecret.',
|
||||
tier: CapabilityTier.supported,
|
||||
),
|
||||
const PlatformCapability(
|
||||
feature: 'Desktop environment',
|
||||
description: 'DE-specific behaviour: screen locker may suspend audio.',
|
||||
tier: CapabilityTier.degraded,
|
||||
),
|
||||
];
|
||||
Reference in New Issue
Block a user