mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-09-16 10:35:02 -04:00
developer menu
This commit is contained in:
parent
8075b81742
commit
ee94f6622c
41 changed files with 294 additions and 35 deletions
|
@ -20,7 +20,6 @@ class VeilidChatApp extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final router = ref.watch(routerProvider);
|
||||
|
||||
final localizationDelegate = LocalizedApp.of(context).delegate;
|
||||
|
||||
return ThemeProvider(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -83,10 +84,16 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
|||
Future<void> Function({required Uint8List inviteData})
|
||||
validateInviteData) {
|
||||
final theme = Theme.of(context);
|
||||
//final scale = theme.extension<ScaleScheme>()!;
|
||||
final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
//final textTheme = theme.textTheme;
|
||||
//final height = MediaQuery.of(context).size.height;
|
||||
|
||||
final monoStyle = TextStyle(
|
||||
fontFamily: 'Source Code Pro',
|
||||
fontSize: 11,
|
||||
color: scale.primaryScale.text,
|
||||
);
|
||||
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Text(
|
||||
translate('paste_invite_dialog.paste_invite_here'),
|
||||
|
@ -97,8 +104,7 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
|||
enabled: !dialogState.isValidating,
|
||||
onChanged: (text) async =>
|
||||
_onPasteChanged(text, validateInviteData),
|
||||
style: textTheme.labelSmall!
|
||||
.copyWith(fontFamily: 'Victor Mono', fontSize: 11),
|
||||
style: monoStyle,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
controller: _pasteTextController,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:signal_strength_indicator/signal_strength_indicator.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../providers/connection_state.dart';
|
||||
import '../tools/tools.dart';
|
||||
|
@ -48,13 +49,17 @@ class SignalStrengthMeterWidget extends ConsumerWidget {
|
|||
}
|
||||
inactiveColor = scale.grayScale.subtleText;
|
||||
|
||||
return SignalStrengthIndicator.bars(
|
||||
value: value,
|
||||
activeColor: color,
|
||||
inactiveColor: inactiveColor,
|
||||
size: iconSize,
|
||||
barCount: 4,
|
||||
spacing: 1,
|
||||
);
|
||||
return GestureDetector(
|
||||
onLongPress: () async {
|
||||
await context.push('/developer');
|
||||
},
|
||||
child: SignalStrengthIndicator.bars(
|
||||
value: value,
|
||||
activeColor: color,
|
||||
inactiveColor: inactiveColor,
|
||||
size: iconSize,
|
||||
barCount: 4,
|
||||
spacing: 1,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
|
||||
import 'app.dart';
|
||||
import 'providers/window_control.dart';
|
||||
|
@ -36,13 +37,18 @@ void main() async {
|
|||
// Make localization delegate
|
||||
final delegate = await LocalizationDelegate.create(
|
||||
fallbackLocale: 'en_US', supportedLocales: ['en_US']);
|
||||
await initializeDateFormatting();
|
||||
|
||||
// Start up Veilid and Veilid processor in the background
|
||||
unawaited(initializeVeilid());
|
||||
|
||||
// Run the app
|
||||
// Hot reloads will only restart this part, not Veilid
|
||||
runApp(ProviderScope(
|
||||
observers: const [StateLogger()],
|
||||
child: LocalizedApp(delegate, VeilidChatApp(theme: initTheme))));
|
||||
runZonedGuarded(() {
|
||||
runApp(ProviderScope(
|
||||
observers: const [StateLogger()],
|
||||
child: LocalizedApp(delegate, VeilidChatApp(theme: initTheme))));
|
||||
}, (error, stackTrace) {
|
||||
log.error('Dart Runtime: {$error}\n{$stackTrace}');
|
||||
});
|
||||
}
|
||||
|
|
166
lib/pages/developer.dart
Normal file
166
lib/pages/developer.dart
Normal file
|
@ -0,0 +1,166 @@
|
|||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:xterm/xterm.dart';
|
||||
|
||||
import '../tools/tools.dart';
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
|
||||
final globalDebugTerminal = Terminal(
|
||||
maxLines: 50000,
|
||||
);
|
||||
|
||||
const kDefaultTerminalStyle = TerminalStyle(
|
||||
fontSize: 11,
|
||||
// height: 1.2,
|
||||
fontFamily: 'Source Code Pro');
|
||||
|
||||
class DeveloperPage extends ConsumerStatefulWidget {
|
||||
const DeveloperPage({super.key});
|
||||
|
||||
@override
|
||||
DeveloperPageState createState() => DeveloperPageState();
|
||||
}
|
||||
|
||||
class DeveloperPageState extends ConsumerState<DeveloperPage> {
|
||||
final terminalController = TerminalController();
|
||||
|
||||
final TextEditingController _debugCommandController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// _scrollController = ScrollController(
|
||||
// onAttach: _handlePositionAttach,
|
||||
// onDetach: _handlePositionDetach,
|
||||
// );
|
||||
super.initState();
|
||||
terminalController.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
// void _handleScrollChange() {
|
||||
// if (_isScrolling != _scrollController.position.isScrollingNotifier.value) {
|
||||
// _isScrolling = _scrollController.position.isScrollingNotifier.value;
|
||||
// _wantsBottom = _scrollController.position.pixels ==
|
||||
// _scrollController.position.maxScrollExtent;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void _handlePositionAttach(ScrollPosition position) {
|
||||
// // From here, add a listener to the given ScrollPosition.
|
||||
// // Here the isScrollingNotifier will be used to inform when scrolling starts
|
||||
// // and stops and change the AppBar's color in response.
|
||||
// position.isScrollingNotifier.addListener(_handleScrollChange);
|
||||
// }
|
||||
|
||||
// void _handlePositionDetach(ScrollPosition position) {
|
||||
// // From here, add a listener to the given ScrollPosition.
|
||||
// // Here the isScrollingNotifier will be used to inform when scrolling starts
|
||||
// // and stops and change the AppBar's color in response.
|
||||
// position.isScrollingNotifier.removeListener(_handleScrollChange);
|
||||
// }
|
||||
|
||||
// void _scrollToBottom() {
|
||||
// _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
||||
// _wantsBottom = true;
|
||||
// }
|
||||
|
||||
Future<void> _sendDebugCommand(String debugCommand) async {
|
||||
log.info('DEBUG >>>\n$debugCommand');
|
||||
final out = await Veilid.instance.debug(debugCommand);
|
||||
log.info('<<< DEBUG\n$out');
|
||||
}
|
||||
|
||||
Future<void> copySelection(BuildContext context) async {
|
||||
final selection = terminalController.selection;
|
||||
if (selection != null) {
|
||||
final text = globalDebugTerminal.buffer.getText(selection);
|
||||
terminalController.clearSelection();
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
if (context.mounted) {
|
||||
showInfoToast(context, translate('developer.copied'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
//final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// if (!_isScrolling && _wantsBottom) {
|
||||
// _scrollToBottom();
|
||||
// }
|
||||
// });
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: scale.primaryScale.text),
|
||||
onPressed: () => GoRouterHelper(context).pop(),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy),
|
||||
color: scale.primaryScale.text,
|
||||
disabledColor: scale.grayScale.subtleText,
|
||||
onPressed: terminalController.selection == null
|
||||
? null
|
||||
: () async {
|
||||
await copySelection(context);
|
||||
})
|
||||
],
|
||||
title: Text(translate('developer.title')),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Column(children: [
|
||||
TerminalView(
|
||||
globalDebugTerminal,
|
||||
textStyle: kDefaultTerminalStyle,
|
||||
controller: terminalController,
|
||||
//autofocus: true,
|
||||
//backgroundOpacity: 0.9,
|
||||
onSecondaryTapDown: (details, offset) async {
|
||||
await copySelection(context);
|
||||
},
|
||||
).expanded(),
|
||||
TextField(
|
||||
controller: _debugCommandController,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: scale.primaryScale.border)),
|
||||
fillColor: scale.primaryScale.subtleBackground,
|
||||
hintText: translate('developer.command'),
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.send),
|
||||
onPressed: () async {
|
||||
final debugCommand = _debugCommandController.text;
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
)),
|
||||
onSubmitted: (debugCommand) async {
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
).paddingAll(4)
|
||||
]));
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<TerminalController>(
|
||||
'terminalController', terminalController));
|
||||
}
|
||||
}
|
|
@ -12,6 +12,13 @@ class IndexPage extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ref.watch(windowControlProvider);
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final textTheme = theme.textTheme;
|
||||
final monoTextStyle = textTheme.labelSmall!
|
||||
.copyWith(fontFamily: 'Source Code Pro', fontSize: 11);
|
||||
final emojiTextStyle = textTheme.labelSmall!
|
||||
.copyWith(fontFamily: 'Noto Color Emoji', fontSize: 11);
|
||||
|
||||
return Scaffold(
|
||||
body: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
|
@ -28,6 +35,11 @@ class IndexPage extends ConsumerWidget {
|
|||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Hack to preload fonts
|
||||
Offstage(child: Text('🧱', style: emojiTextStyle)),
|
||||
// Hack to preload fonts
|
||||
Offstage(child: Text('A', style: monoTextStyle)),
|
||||
// Splash Screen
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: SvgPicture.asset(
|
||||
|
|
|
@ -21,8 +21,8 @@ import '../../entities/local_account.dart';
|
|||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
import '../../veilid_support/veilid_support.dart';
|
||||
import 'account_page.dart';
|
||||
import 'chats_page.dart';
|
||||
import 'account.dart';
|
||||
import 'chats.dart';
|
||||
|
||||
class MainPager extends ConsumerStatefulWidget {
|
||||
const MainPager(
|
||||
|
|
|
@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../pages/chat_only_page.dart';
|
||||
import '../pages/chat_only.dart';
|
||||
import '../pages/developer.dart';
|
||||
import '../pages/home.dart';
|
||||
import '../pages/index.dart';
|
||||
import '../pages/new_account.dart';
|
||||
|
@ -126,6 +127,10 @@ class RouterNotifier extends _$RouterNotifier implements Listenable {
|
|||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/developer',
|
||||
builder: (context, state) => const DeveloperPage(),
|
||||
)
|
||||
];
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -6,7 +6,7 @@ part of 'router_notifier.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$routerNotifierHash() => r'b7c49cdc6f940c5a5032faaf19af08d9f478dae6';
|
||||
String _$routerNotifierHash() => r'5a3527e3890f0746db4cbe051d453b89e5809989';
|
||||
|
||||
/// See also [RouterNotifier].
|
||||
@ProviderFor(RouterNotifier)
|
||||
|
|
|
@ -2,8 +2,10 @@ import 'dart:io' show Platform;
|
|||
|
||||
import 'package:ansicolor/ansicolor.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:loggy/loggy.dart';
|
||||
|
||||
import '../pages/developer.dart';
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
|
||||
String wrapWithLogColor(LogLevel? level, String text) {
|
||||
|
@ -47,14 +49,33 @@ String wrapWithLogColor(LogLevel? level, String text) {
|
|||
return text;
|
||||
}
|
||||
|
||||
final DateFormat _dateFormatter = DateFormat('HH:mm:ss.SSS');
|
||||
|
||||
extension PrettyPrintLogRecord on LogRecord {
|
||||
String pretty() {
|
||||
final lstr =
|
||||
wrapWithLogColor(level, '[${level.toString().substring(0, 1)}]');
|
||||
return '$lstr $message';
|
||||
final tm = _dateFormatter.format(time.toLocal());
|
||||
final lev = logEmoji(level);
|
||||
final lstr = wrapWithLogColor(level, tm);
|
||||
return '$lstr $lev $message';
|
||||
}
|
||||
}
|
||||
|
||||
String logEmoji(LogLevel logLevel) {
|
||||
switch (logLevel) {
|
||||
case traceLevel:
|
||||
return '👾';
|
||||
case LogLevel.debug:
|
||||
return '🐛';
|
||||
case LogLevel.info:
|
||||
return '💡';
|
||||
case LogLevel.warning:
|
||||
return '⚠️';
|
||||
case LogLevel.error:
|
||||
return '🛑';
|
||||
}
|
||||
return '❓';
|
||||
}
|
||||
|
||||
class CallbackPrinter extends LoggyPrinter {
|
||||
CallbackPrinter() : super();
|
||||
|
||||
|
@ -62,7 +83,9 @@ class CallbackPrinter extends LoggyPrinter {
|
|||
|
||||
@override
|
||||
void onLog(LogRecord record) {
|
||||
debugPrint(record.pretty());
|
||||
final out = record.pretty();
|
||||
debugPrint(out);
|
||||
globalDebugTerminal.write('$out\n'.replaceAll('\n', '\r\n'));
|
||||
callback?.call(record);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ void _initVeilid() {
|
|||
const platformConfig = VeilidFFIConfig(
|
||||
logging: VeilidFFIConfigLogging(
|
||||
terminal: VeilidFFIConfigLoggingTerminal(
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
level: VeilidConfigLogLevel.debug,
|
||||
),
|
||||
otlp: VeilidFFIConfigLoggingOtlp(
|
||||
|
|
|
@ -20,6 +20,7 @@ Future<VeilidConfig> getVeilidChatConfig() async {
|
|||
|
||||
return config.copyWith(
|
||||
capabilities: const VeilidConfigCapabilities(disable: ['DHTV', 'TUNL']),
|
||||
protectedStore: config.protectedStore.copyWith(allowInsecureFallback: true),
|
||||
// network: config.network.copyWith(
|
||||
// dht: config.network.dht.copyWith(
|
||||
// getValueCount: 3,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue