refactor: streamline terminal bottom controls
This commit is contained in:
parent
2dd5113044
commit
12f2b7bb8c
@ -5,13 +5,15 @@ import 'package:flutter/material.dart';
|
||||
class RepeatableTerminalKeyButton extends StatefulWidget {
|
||||
const RepeatableTerminalKeyButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.label,
|
||||
this.icon,
|
||||
this.enabled = true,
|
||||
this.repeatable = false,
|
||||
});
|
||||
}) : assert(label != null || icon != null);
|
||||
|
||||
final String label;
|
||||
final String? label;
|
||||
final IconData? icon;
|
||||
final VoidCallback onPressed;
|
||||
final bool enabled;
|
||||
final bool repeatable;
|
||||
@ -61,11 +63,15 @@ class _RepeatableTerminalKeyButtonState
|
||||
backgroundColor: const Color(0xFF151A20),
|
||||
side: const BorderSide(color: Color(0xFF3F3428)),
|
||||
minimumSize: const Size(0, 34),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: widget.label == null ? 8 : 10,
|
||||
),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: Text(widget.label),
|
||||
child: widget.icon != null
|
||||
? Icon(widget.icon, size: 18)
|
||||
: Text(widget.label!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -92,24 +92,28 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
keyId: 'up',
|
||||
label: 'Up',
|
||||
input: '\u001b[A',
|
||||
icon: Icons.arrow_upward,
|
||||
repeatable: true,
|
||||
),
|
||||
_QuickTerminalKey(
|
||||
keyId: 'down',
|
||||
label: 'Down',
|
||||
input: '\u001b[B',
|
||||
icon: Icons.arrow_downward,
|
||||
repeatable: true,
|
||||
),
|
||||
_QuickTerminalKey(
|
||||
keyId: 'left',
|
||||
label: 'Left',
|
||||
input: '\u001b[D',
|
||||
icon: Icons.arrow_back,
|
||||
repeatable: true,
|
||||
),
|
||||
_QuickTerminalKey(
|
||||
keyId: 'right',
|
||||
label: 'Right',
|
||||
input: '\u001b[C',
|
||||
icon: Icons.arrow_forward,
|
||||
repeatable: true,
|
||||
),
|
||||
];
|
||||
@ -142,6 +146,7 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
bool _awaitingAttachReplayFrame = true;
|
||||
bool _awaitingReconnectRestore = false;
|
||||
bool _shouldReconnectOnResume = false;
|
||||
bool _showExpandedControls = false;
|
||||
_TerminalInputMode _inputMode = _TerminalInputMode.read;
|
||||
TerminalConnectionState? _lastConnectionState;
|
||||
|
||||
@ -558,127 +563,6 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showActionsSheet() async {
|
||||
var presets = const <PresetCommand>[];
|
||||
try {
|
||||
presets = await ref.read(presetRepositoryProvider).listPresets();
|
||||
} catch (_) {
|
||||
presets = const <PresetCommand>[];
|
||||
}
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
return showModalBottomSheet<void>(
|
||||
context: context,
|
||||
backgroundColor: const Color(0xFF13191F),
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return SafeArea(
|
||||
child: AnimatedBuilder(
|
||||
animation: _pageStateListenable,
|
||||
builder: (context, _) {
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
|
||||
child: Column(
|
||||
key: const Key('terminal_actions_sheet'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Terminal actions',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const Spacer(),
|
||||
StatusPill(
|
||||
label: _statusLabel,
|
||||
icon: _statusIcon,
|
||||
color: _statusColor(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Session',
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_jumpToBottom();
|
||||
},
|
||||
icon: const Icon(Icons.vertical_align_bottom),
|
||||
label: const Text('Latest'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
unawaited(_coordinator.reconnectNow());
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Reconnect'),
|
||||
),
|
||||
if (widget.project != null)
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
unawaited(_openSiblingTerminal());
|
||||
},
|
||||
icon: const Icon(Icons.add_box_outlined),
|
||||
label: const Text('New terminal'),
|
||||
),
|
||||
TextButton.icon(
|
||||
key: const Key('terminal_diagnostics_button'),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
await _showDiagnostics();
|
||||
},
|
||||
icon: const Icon(Icons.bug_report_outlined),
|
||||
label: const Text('Diagnostics'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Presets',
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
PresetPanel(
|
||||
presets: presets,
|
||||
onPresetSelected: (preset) {
|
||||
Navigator.of(context).pop();
|
||||
unawaited(_sendLine(preset.commandText));
|
||||
},
|
||||
onManagePressed: () {
|
||||
Navigator.of(context).pop();
|
||||
unawaited(_openPresetManagementPage());
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
_coordinator.connectionStatus,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openPresetManagementPage() {
|
||||
return Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const PresetManagementPage()),
|
||||
@ -688,7 +572,6 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = MediaQuery.sizeOf(context).width;
|
||||
final isCompact = width < 420;
|
||||
final isTight = width < 400;
|
||||
final workingDirectory =
|
||||
widget.project?.workingDirectory ??
|
||||
@ -722,67 +605,14 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AnimatedBuilder(
|
||||
animation: controller,
|
||||
builder: (context, _) {
|
||||
final mode = controller.isFollowingLiveOutput
|
||||
? 'Live'
|
||||
: 'Scrollback';
|
||||
final modeLabel = isCompact
|
||||
? mode
|
||||
: '$mode | ${controller.liveLines.length} lines';
|
||||
final statusLabel = isCompact
|
||||
? _compactStatusLabel
|
||||
: _statusLabel;
|
||||
|
||||
return Row(
|
||||
key: const Key('terminal_status_summary'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: controller.isFollowingLiveOutput
|
||||
? controller.enterScrollback
|
||||
: controller.jumpToLive,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFD8C4A0),
|
||||
minimumSize: const Size(0, 32),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: Text(modeLabel),
|
||||
),
|
||||
SizedBox(width: isCompact ? 2 : 4),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: isCompact ? 4 : 8),
|
||||
child: Center(
|
||||
child: StatusPill(
|
||||
label: statusLabel,
|
||||
icon: _statusIcon,
|
||||
color: _statusColor(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
key: const Key('terminal_actions_button'),
|
||||
onPressed: _showActionsSheet,
|
||||
visualDensity: VisualDensity.compact,
|
||||
icon: const Icon(Icons.tune),
|
||||
tooltip: 'Show actions',
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
actions: const [],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
minimum: AppTheme.pagePadding,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildScrollbackSection(context, isCompact),
|
||||
_buildScrollbackSection(context),
|
||||
Expanded(
|
||||
child: Container(
|
||||
key: const Key('terminal_surface_panel'),
|
||||
@ -819,14 +649,15 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
_buildCommandDeck(context, isCompact),
|
||||
_buildCommandDeck(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildScrollbackSection(BuildContext context, bool isCompact) {
|
||||
Widget _buildScrollbackSection(BuildContext context) {
|
||||
final isCompact = MediaQuery.sizeOf(context).width < 420;
|
||||
return AnimatedBuilder(
|
||||
animation: _pageStateListenable,
|
||||
builder: (context, _) {
|
||||
@ -922,7 +753,7 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
borderRadius: BorderRadius.zero,
|
||||
border: Border.all(color: const Color(0xFF2A231B)),
|
||||
),
|
||||
child: isCompact
|
||||
child: MediaQuery.sizeOf(context).width < 420
|
||||
? _buildCompactHistoryActions(context)
|
||||
: _buildWideHistoryActions(context),
|
||||
),
|
||||
@ -1045,7 +876,7 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommandDeck(BuildContext context, bool isCompact) {
|
||||
Widget _buildCommandDeck(BuildContext context) {
|
||||
return AppPanel(
|
||||
key: const Key('terminal_command_deck'),
|
||||
tone: AppPanelTone.emphasis,
|
||||
@ -1095,7 +926,13 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildQuickKeysSection(),
|
||||
_buildPinnedQuickKeysRow(),
|
||||
if (_showExpandedControls) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildExpandedControls(context),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
_buildStatusRow(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -1103,38 +940,167 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuickKeysSection() {
|
||||
return Column(
|
||||
Widget _buildPinnedQuickKeysRow() {
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_buildKeyTrayRow(_editingControlKeys),
|
||||
const SizedBox(height: 8),
|
||||
_buildKeyTrayRow(_navigationKeys),
|
||||
const SizedBox(height: 8),
|
||||
_buildKeyTrayRow(_symbolTerminalKeys),
|
||||
_buildMoreControlsButton(),
|
||||
..._navigationKeys
|
||||
.where((key) => switch (key.keyId) {
|
||||
'up' || 'down' || 'left' || 'right' => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(_buildQuickKeyButton),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExpandedControls(BuildContext context) {
|
||||
final presetsFuture = ref.watch(presetRepositoryProvider).listPresets();
|
||||
return FutureBuilder<List<PresetCommand>>(
|
||||
future: presetsFuture,
|
||||
builder: (context, snapshot) {
|
||||
final presets = snapshot.data ?? const <PresetCommand>[];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildKeyTrayRow(_editingControlKeys),
|
||||
const SizedBox(height: 8),
|
||||
_buildKeyTrayRow(
|
||||
_navigationKeys.where((key) {
|
||||
return key.keyId != 'up' &&
|
||||
key.keyId != 'down' &&
|
||||
key.keyId != 'left' &&
|
||||
key.keyId != 'right';
|
||||
}).toList(growable: false),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildKeyTrayRow(_symbolTerminalKeys),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
key: const Key('terminal_latest_inline_button'),
|
||||
onPressed: _jumpToBottom,
|
||||
icon: const Icon(Icons.vertical_align_bottom),
|
||||
label: const Text('Latest'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
key: const Key('terminal_reconnect_inline_button'),
|
||||
onPressed: () => unawaited(_coordinator.reconnectNow()),
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Reconnect'),
|
||||
),
|
||||
if (widget.project != null)
|
||||
OutlinedButton.icon(
|
||||
key: const Key('terminal_new_inline_button'),
|
||||
onPressed: () => unawaited(_openSiblingTerminal()),
|
||||
icon: const Icon(Icons.add_box_outlined),
|
||||
label: const Text('New terminal'),
|
||||
),
|
||||
TextButton.icon(
|
||||
key: const Key('terminal_diagnostics_inline_button'),
|
||||
onPressed: _showDiagnostics,
|
||||
icon: const Icon(Icons.bug_report_outlined),
|
||||
label: const Text('Diagnostics'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
PresetPanel(
|
||||
presets: presets,
|
||||
onPresetSelected: (preset) {
|
||||
unawaited(_sendLine(preset.commandText));
|
||||
},
|
||||
onManagePressed: () {
|
||||
unawaited(_openPresetManagementPage());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildKeyTrayRow(List<_QuickTerminalKey> keys) {
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: keys
|
||||
.map((quickKey) {
|
||||
return _buildCommandDeckAction(
|
||||
RepeatableTerminalKeyButton(
|
||||
key: Key('terminal_quick_key_${quickKey.keyId}'),
|
||||
enabled: _canSendInput,
|
||||
repeatable: quickKey.repeatable,
|
||||
label: quickKey.label,
|
||||
onPressed: () => _sendQuickKey(quickKey),
|
||||
),
|
||||
);
|
||||
})
|
||||
.map(_buildQuickKeyButton)
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuickKeyButton(_QuickTerminalKey quickKey) {
|
||||
return _buildCommandDeckAction(
|
||||
RepeatableTerminalKeyButton(
|
||||
key: Key('terminal_quick_key_${quickKey.keyId}'),
|
||||
enabled: _canSendInput,
|
||||
repeatable: quickKey.repeatable,
|
||||
label: quickKey.label,
|
||||
icon: quickKey.icon,
|
||||
onPressed: () => _sendQuickKey(quickKey),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMoreControlsButton() {
|
||||
return _buildCommandDeckAction(
|
||||
OutlinedButton.icon(
|
||||
key: const Key('terminal_more_controls_button'),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_showExpandedControls = !_showExpandedControls;
|
||||
});
|
||||
},
|
||||
icon: Icon(_showExpandedControls ? Icons.expand_less : Icons.expand_more),
|
||||
label: Text(_showExpandedControls ? 'Less' : 'More'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusRow(BuildContext context) {
|
||||
final mode = controller.isFollowingLiveOutput ? 'Live' : 'Scrollback';
|
||||
final modeLabel = '$mode | ${controller.liveLines.length} lines';
|
||||
return Wrap(
|
||||
key: const Key('terminal_status_summary'),
|
||||
spacing: 8,
|
||||
runSpacing: 6,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: controller.isFollowingLiveOutput
|
||||
? controller.enterScrollback
|
||||
: controller.jumpToLive,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFD8C4A0),
|
||||
minimumSize: const Size(0, 32),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: Text(modeLabel),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
StatusPill(
|
||||
label: _statusLabel,
|
||||
icon: _statusIcon,
|
||||
color: _statusColor(context),
|
||||
),
|
||||
Text(
|
||||
_coordinator.connectionStatus,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommandDeckAction(Widget child) {
|
||||
return ExcludeFocus(child: child);
|
||||
}
|
||||
@ -1172,13 +1138,6 @@ class _TerminalPageState extends ConsumerState<TerminalPage>
|
||||
TerminalConnectionState.disconnected => 'Offline',
|
||||
};
|
||||
|
||||
String get _compactStatusLabel => switch (_connectionState) {
|
||||
TerminalConnectionState.connecting => 'Sync',
|
||||
TerminalConnectionState.connected => 'On',
|
||||
TerminalConnectionState.reconnecting => 'Sync',
|
||||
TerminalConnectionState.disconnected => 'Off',
|
||||
};
|
||||
|
||||
bool get _canSendInput =>
|
||||
controller.canSendInput && _inputMode == _TerminalInputMode.edit;
|
||||
|
||||
@ -1208,12 +1167,14 @@ class _QuickTerminalKey {
|
||||
required this.keyId,
|
||||
required this.label,
|
||||
required this.input,
|
||||
this.icon,
|
||||
this.repeatable = false,
|
||||
});
|
||||
|
||||
final String keyId;
|
||||
final String label;
|
||||
final String input;
|
||||
final IconData? icon;
|
||||
final bool repeatable;
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ void main() {
|
||||
expect(find.byKey(const Key('terminal_surface_panel')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_command_deck')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_status_summary')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_actions_button')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_more_controls_button')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('project list deletes a project after confirmation', (
|
||||
@ -348,7 +348,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'terminal page shows mode controls and inline quick keys in the command deck',
|
||||
'terminal page keeps the action entry in the command deck instead of the app bar',
|
||||
(tester) async {
|
||||
await _pumpApp(
|
||||
tester,
|
||||
@ -364,14 +364,18 @@ void main() {
|
||||
expect(find.byType(TextField), findsNothing);
|
||||
expect(find.byKey(const Key('terminal_mode_read_button')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_mode_edit_button')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_ctrl_c')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_up')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_enter')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_actions_button')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_down')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_left')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_right')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_more_controls_button')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_actions_button')), findsNothing);
|
||||
expect(find.byKey(const Key('terminal_quick_key_ctrl_c')), findsNothing);
|
||||
expect(find.byKey(const Key('terminal_quick_key_enter')), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('terminal command deck exposes expanded quick terminal keys', (tester) async {
|
||||
testWidgets('terminal more controls button toggles expanded quick terminal keys', (tester) async {
|
||||
final transportFactory = _QueuedTerminalSocketTransportFactory();
|
||||
|
||||
await _pumpApp(
|
||||
@ -385,6 +389,16 @@ void main() {
|
||||
|
||||
await _openProjectTerminal(tester);
|
||||
|
||||
expect(find.byKey(const Key('terminal_quick_key_up')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_down')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_left')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_right')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_esc')), findsNothing);
|
||||
expect(find.byKey(const Key('terminal_quick_key_ctrl_c')), findsNothing);
|
||||
|
||||
await tester.tap(find.byKey(const Key('terminal_more_controls_button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('terminal_quick_key_esc')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_tab')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_ctrl_c')), findsOneWidget);
|
||||
@ -396,10 +410,7 @@ void main() {
|
||||
expect(find.byKey(const Key('terminal_quick_key_end')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_page_up')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_page_down')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_up')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_down')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_left')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_right')), findsOneWidget);
|
||||
expect(find.byKey(const Key('terminal_quick_key_enter')), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('terminal_mode_edit_button')));
|
||||
await tester.pumpAndSettle();
|
||||
@ -427,6 +438,10 @@ void main() {
|
||||
expect(find.byKey(const Key('terminal_mode_button')), findsOneWidget);
|
||||
expect(find.text('Read'), findsOneWidget);
|
||||
expect(find.text('Edit'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.arrow_upward), findsWidgets);
|
||||
expect(find.byIcon(Icons.arrow_downward), findsWidgets);
|
||||
expect(find.byIcon(Icons.arrow_back), findsWidgets);
|
||||
expect(find.byIcon(Icons.arrow_forward), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('terminal quick keys stay disabled until edit mode is selected', (
|
||||
@ -445,6 +460,8 @@ void main() {
|
||||
|
||||
await _openProjectTerminal(tester);
|
||||
|
||||
await tester.tap(find.byKey(const Key('terminal_more_controls_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_quick_key_ctrl_l')));
|
||||
await tester.pump();
|
||||
|
||||
@ -503,14 +520,14 @@ void main() {
|
||||
await _openProjectTerminal(tester);
|
||||
await tester.tap(find.byKey(const Key('terminal_mode_edit_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_more_controls_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_quick_key_ctrl_l')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
transportFactory.createdTransports.single.emit('command-output');
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_actions_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_diagnostics_button')));
|
||||
await tester.tap(find.byKey(const Key('terminal_diagnostics_inline_button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.textContaining('ui.input.quick | Ctrl+L'), findsOneWidget);
|
||||
@ -537,6 +554,8 @@ void main() {
|
||||
await _openProjectTerminal(tester);
|
||||
await tester.tap(find.byKey(const Key('terminal_mode_edit_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_more_controls_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('terminal_quick_key_enter')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -568,9 +587,6 @@ void main() {
|
||||
await transportFactory.createdTransports.first.close();
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(const Key('terminal_actions_button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Connection lost. Reconnecting...'), findsOneWidget);
|
||||
|
||||
await tester.pump(const Duration(seconds: 2));
|
||||
@ -659,9 +675,9 @@ void main() {
|
||||
await _openProjectTerminal(tester);
|
||||
expect(sessionRepository.createCount, 1);
|
||||
|
||||
await tester.tap(find.byKey(const Key('terminal_actions_button')));
|
||||
await tester.tap(find.byKey(const Key('terminal_more_controls_button')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('New terminal'));
|
||||
await tester.tap(find.byKey(const Key('terminal_new_inline_button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(sessionRepository.createCount, 2);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user