Vydané zdroje
Stáhnout zdroje ZIP
CH-J SM Mobile
Procházejte adresáře a soubory konkrétního vydání aplikace.
CH-J / SM Mobile
175 souborů
sm-mobile-20261016T185235Z-8e93ecc1b59a
Přidáno: · Sestaveno:
1
import 'package:flutter/foundation.dart';2
import 'package:flutter/material.dart';3
import 'package:flutter/services.dart';4
import 'package:xterm/xterm.dart';5
import '../core/app_controller.dart';6
import '../l10n/strings.dart';7
import 'adaptive.dart';8
import 'common.dart';9
import 'theme.dart';11
// xterm's default palette on the desktop terminal background.12
const _terminalTheme = TerminalTheme(13
cursor: Color(0xccecf5ff),14
selection: Color(0x6642a5ff),15
foreground: Color(0xffdce8f5),16
background: Brand.terminal,17
black: Color(0xff000000),18
red: Color(0xffcd3131),19
green: Color(0xff0dbc79),20
yellow: Color(0xffe5e510),21
blue: Color(0xff2472c8),22
magenta: Color(0xffbc3fbc),23
cyan: Color(0xff11a8cd),24
white: Color(0xffe5e5e5),25
brightBlack: Color(0xff666666),26
brightRed: Color(0xfff14c4c),27
brightGreen: Color(0xff23d18b),28
brightYellow: Color(0xfff5f543),29
brightBlue: Color(0xff3b8eea),30
brightMagenta: Color(0xffd670d6),31
brightCyan: Color(0xff29b8db),32
brightWhite: Color(0xffffffff),33
searchHitBackground: Color(0xffffff2b),34
searchHitBackgroundCurrent: Color(0xff31ff26),35
searchHitForeground: Color(0xff000000),36
);38
class TerminalPage extends StatefulWidget {39
final AppController controller;40
const TerminalPage(this.controller, {super.key});41
@override42
State<TerminalPage> createState() => _TerminalPageState();43
}45
class _TerminalPageState extends State<TerminalPage> {46
final selection = TerminalController();47
final focus = FocusNode();48
bool ctrl = false, alt = false;49
@override50
void initState() {51
super.initState();52
// The keyboard button offers show or hide depending on focus.53
focus.addListener(_focusChanged);54
}56
void _focusChanged() {57
if (mounted) setState(() {});58
}60
@override61
void dispose() {62
focus.removeListener(_focusChanged);63
selection.dispose();64
focus.dispose();65
super.dispose();66
}68
@override69
Widget build(BuildContext context) {70
final c = widget.controller;71
if (!c.ssh.connected) {72
return const PageMessage('selectServer', icon: Icons.terminal);73
}74
final terminal = c.ssh.terminal;75
// Key bar plus at least a few terminal rows; shorter space scrolls.76
return MinHeight(77
minHeight: 120,78
child: Column(79
children: [80
Expanded(81
child: Focus(82
onKeyEvent: (_, _) {83
c.activity();84
return KeyEventResult.ignored;85
},86
// xterm paints lines without clipping; scrolled-out lines87
// would otherwise draw over the status row and app bar.88
child: ClipRect(89
child: ColoredBox(90
color: Brand.terminal,91
child: TerminalView(92
terminal,93
controller: selection,94
focusNode: focus,95
theme: _terminalTheme,96
// Desktop terminal insets; narrower on compact phones.97
padding: MediaQuery.sizeOf(context).width < 60098
? const EdgeInsets.fromLTRB(8, 6, 4, 4)99
: const EdgeInsets.fromLTRB(14, 12, 8, 8),100
// Soft keyboards (iOS, many Android IMEs) send no backspace101
// key event on an empty input buffer; this detects deletion.102
deleteDetection:103
defaultTargetPlatform == TargetPlatform.iOS ||104
defaultTargetPlatform == TargetPlatform.android,105
textStyle: TerminalStyle(106
fontSize: 14 * MediaQuery.textScalerOf(context).scale(1),107
),108
onKeyEvent: (_, key) {109
c.activity();110
return KeyEventResult.ignored;111
},112
),113
),114
),115
),116
),117
// Key bar styled like the desktop terminal toolbar.118
DecoratedBox(119
decoration: BoxDecoration(120
color: Theme.of(context).navigationBarTheme.backgroundColor,121
border: Border(122
top: BorderSide(color: Theme.of(context).dividerColor),123
),124
),125
child: SingleChildScrollView(126
scrollDirection: Axis.horizontal,127
padding: const EdgeInsets.symmetric(horizontal: 4),128
child: Row(129
children: [130
for (final entry in {131
'ESC': TerminalKey.escape,132
'TAB': TerminalKey.tab,133
'↑': TerminalKey.arrowUp,134
'↓': TerminalKey.arrowDown,135
'←': TerminalKey.arrowLeft,136
'→': TerminalKey.arrowRight,137
}.entries)138
TextButton(139
onPressed: () {140
c.activity();141
terminal.keyInput(entry.value);142
},143
child: Text(entry.key),144
),145
FilterChip(146
label: const Text('CTRL'),147
selected: c.ssh.ctrlNext,148
onSelected: (v) => setState(() => c.ssh.ctrlNext = v),149
),150
const SizedBox(width: 6),151
FilterChip(152
label: const Text('ALT'),153
selected: c.ssh.altNext,154
onSelected: (v) => setState(() => c.ssh.altNext = v),155
),156
IconButton(157
tooltip: context.tr(158
focus.hasFocus ? 'hideKeyboard' : 'keyboard',159
),160
icon: Icon(161
focus.hasFocus ? Icons.keyboard_hide : Icons.keyboard,162
),163
onPressed: () =>164
focus.hasFocus ? focus.unfocus() : focus.requestFocus(),165
),166
IconButton(167
tooltip: context.tr('copy'),168
icon: const Icon(Icons.copy),169
onPressed: () {170
if (selection.selection != null) {171
Clipboard.setData(172
ClipboardData(173
text: terminal.buffer.getText(selection.selection),174
),175
);176
}177
},178
),179
IconButton(180
tooltip: context.tr('paste'),181
icon: const Icon(Icons.paste),182
onPressed: () async {183
final data = await Clipboard.getData(184
Clipboard.kTextPlain,185
);186
if (c.ssh.connected &&187
c.vault.unlocked &&188
data?.text != null) {189
terminal.paste(data!.text!);190
}191
},192
),193
],194
),195
),196
),197
],198
),199
);200
}201
}SHA-256
1f129c620e60f667af0ce40aa55cebad926b99ad2e54e40158ece95f8601aa5eCH-J Proprietary Software License 1.14 · Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.
SHA-256 archivu
46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655