fix: ssh term (#365)

This commit is contained in:
lollipopkit
2024-05-27 11:32:52 +08:00
parent ccab4040b1
commit 1a3cb09ca2
33 changed files with 73 additions and 66 deletions

View File

@@ -16,7 +16,7 @@ class MyApp extends StatelessWidget {
_setup(context); _setup(context);
return ListenableBuilder( return ListenableBuilder(
listenable: RebuildNodes.app, listenable: RebuildNodes.app,
builder: (_, __) { builder: (context, _) {
if (!Stores.setting.useSystemPrimaryColor.fetch()) { if (!Stores.setting.useSystemPrimaryColor.fetch()) {
UIs.colorSeed = Color(Stores.setting.primaryColor.fetch()); UIs.colorSeed = Color(Stores.setting.primaryColor.fetch());
return _buildApp(context); return _buildApp(context);

View File

@@ -280,6 +280,7 @@
"suspend": "Suspend", "suspend": "Suspend",
"suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.", "suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.",
"switchTo": "Wechseln zu {val}", "switchTo": "Wechseln zu {val}",
"sync": "Sync",
"syncTip": "Damit einige Änderungen wirksam werden, kann ein Neustart erforderlich sein.", "syncTip": "Damit einige Änderungen wirksam werden, kann ein Neustart erforderlich sein.",
"system": "Systeme", "system": "Systeme",
"tag": "Tags", "tag": "Tags",

View File

@@ -280,6 +280,7 @@
"suspend": "Suspend", "suspend": "Suspend",
"suspendTip": "The suspend function requires root privileges and systemd support.", "suspendTip": "The suspend function requires root privileges and systemd support.",
"switchTo": "Switch to {val}", "switchTo": "Switch to {val}",
"sync": "Sync",
"syncTip": "A restart may be required for some changes to take effect.", "syncTip": "A restart may be required for some changes to take effect.",
"system": "System", "system": "System",
"tag": "Tags", "tag": "Tags",

View File

@@ -280,6 +280,7 @@
"suspend": "Suspender", "suspend": "Suspender",
"suspendTip": "La función de suspender necesita permisos de root y soporte de systemd.", "suspendTip": "La función de suspender necesita permisos de root y soporte de systemd.",
"switchTo": "Cambiar a {val}", "switchTo": "Cambiar a {val}",
"sync": "Sincronizar",
"syncTip": "Puede que necesites reiniciar para que algunos cambios tengan efecto.", "syncTip": "Puede que necesites reiniciar para que algunos cambios tengan efecto.",
"system": "Sistema", "system": "Sistema",
"tag": "Etiqueta", "tag": "Etiqueta",

View File

@@ -280,6 +280,7 @@
"suspend": "Suspendre", "suspend": "Suspendre",
"suspendTip": "La fonction de suspension nécessite des privilèges root et le support de systemd.", "suspendTip": "La fonction de suspension nécessite des privilèges root et le support de systemd.",
"switchTo": "Passer à {val}", "switchTo": "Passer à {val}",
"sync": "Sync",
"syncTip": "Un redémarrage peut être nécessaire pour que certains changements prennent effet.", "syncTip": "Un redémarrage peut être nécessaire pour que certains changements prennent effet.",
"system": "Système", "system": "Système",
"tag": "Étiquettes", "tag": "Étiquettes",

View File

@@ -280,6 +280,7 @@
"suspend": "Suspend", "suspend": "Suspend",
"suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.", "suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.",
"switchTo": "Beralih ke {val}", "switchTo": "Beralih ke {val}",
"sync": "Sinkronisasi",
"syncTip": "Pengaktifan ulang mungkin diperlukan agar beberapa perubahan dapat diterapkan.", "syncTip": "Pengaktifan ulang mungkin diperlukan agar beberapa perubahan dapat diterapkan.",
"system": "Sistem", "system": "Sistem",
"tag": "Tag", "tag": "Tag",

View File

@@ -280,6 +280,7 @@
"suspend": "中断", "suspend": "中断",
"suspendTip": "suspend機能はroot権限とsystemdのサポートが必要です。", "suspendTip": "suspend機能はroot権限とsystemdのサポートが必要です。",
"switchTo": "{val}に切り替える", "switchTo": "{val}に切り替える",
"sync": "同期する",
"syncTip": "再起動が必要な場合があります。一部の変更はその後に有効になります。", "syncTip": "再起動が必要な場合があります。一部の変更はその後に有効になります。",
"system": "システム", "system": "システム",
"tag": "タグ", "tag": "タグ",

View File

@@ -279,6 +279,7 @@
"suspend": "Ophangen", "suspend": "Ophangen",
"suspendTip": "De opschortfunctie vereist rootrechten en systemd-ondersteuning.", "suspendTip": "De opschortfunctie vereist rootrechten en systemd-ondersteuning.",
"switchTo": "Overschakelen naar {val}", "switchTo": "Overschakelen naar {val}",
"sync": "Sync",
"syncTip": "Een herstart kan nodig zijn voor sommige wijzigingen om van kracht te worden.", "syncTip": "Een herstart kan nodig zijn voor sommige wijzigingen om van kracht te worden.",
"system": "Systeem", "system": "Systeem",
"tag": "Labels", "tag": "Labels",

View File

@@ -280,6 +280,7 @@
"suspend": "Suspender", "suspend": "Suspender",
"suspendTip": "A função de suspensão requer permissões de root e suporte do systemd.", "suspendTip": "A função de suspensão requer permissões de root e suporte do systemd.",
"switchTo": "Mudar para {val}", "switchTo": "Mudar para {val}",
"sync": "Sincronizar",
"syncTip": "Pode ser necessário reiniciar para algumas mudanças surtirem efeito.", "syncTip": "Pode ser necessário reiniciar para algumas mudanças surtirem efeito.",
"system": "Sistema", "system": "Sistema",
"tag": "Tag", "tag": "Tag",

View File

@@ -280,6 +280,7 @@
"suspend": "приостановить", "suspend": "приостановить",
"suspendTip": "Функция приостановки требует прав root и поддержки systemd.", "suspendTip": "Функция приостановки требует прав root и поддержки systemd.",
"switchTo": "переключиться на {val}", "switchTo": "переключиться на {val}",
"sync": "Синхронизировать",
"syncTip": "Возможно, потребуется перезагрузка, чтобы некоторые изменения вступили в силу.", "syncTip": "Возможно, потребуется перезагрузка, чтобы некоторые изменения вступили в силу.",
"system": "система", "system": "система",
"tag": "тег", "tag": "тег",

View File

@@ -280,6 +280,7 @@
"suspend": "挂起", "suspend": "挂起",
"suspendTip": "suspend 功能需要 root 权限及 systemd 支持。", "suspendTip": "suspend 功能需要 root 权限及 systemd 支持。",
"switchTo": "切换到 {val}", "switchTo": "切换到 {val}",
"sync": "同步",
"syncTip": "可能需要重新启动,某些更改才能生效。", "syncTip": "可能需要重新启动,某些更改才能生效。",
"system": "系统", "system": "系统",
"tag": "标签", "tag": "标签",

View File

@@ -280,6 +280,7 @@
"suspend": "挂起", "suspend": "挂起",
"suspendTip": "suspend 功能需要 root 權限及 systemd 支持。", "suspendTip": "suspend 功能需要 root 權限及 systemd 支持。",
"switchTo": "切換到 {val}", "switchTo": "切換到 {val}",
"sync": "同步",
"syncTip": "可能需要重新啟動,某些更改才能生效。", "syncTip": "可能需要重新啟動,某些更改才能生效。",
"system": "系統", "system": "系統",
"tag": "标签", "tag": "标签",

View File

@@ -37,7 +37,7 @@ class EditorPage extends StatefulWidget {
}); });
@override @override
_EditorPageState createState() => _EditorPageState(); State<EditorPage> createState() => _EditorPageState();
} }
class _EditorPageState extends State<EditorPage> { class _EditorPageState extends State<EditorPage> {

View File

@@ -14,7 +14,7 @@ class PingPage extends StatefulWidget {
const PingPage({super.key}); const PingPage({super.key});
@override @override
_PingPageState createState() => _PingPageState(); State<PingPage> createState() => _PingPageState();
} }
class _PingPageState extends State<PingPage> class _PingPageState extends State<PingPage>

View File

@@ -19,7 +19,7 @@ class PrivateKeyEditPage extends StatefulWidget {
final PrivateKeyInfo? pki; final PrivateKeyInfo? pki;
@override @override
_PrivateKeyEditPageState createState() => _PrivateKeyEditPageState(); State<PrivateKeyEditPage> createState() => _PrivateKeyEditPageState();
} }
class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> { class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {

View File

@@ -15,7 +15,7 @@ class PrivateKeysListPage extends StatefulWidget {
const PrivateKeysListPage({super.key}); const PrivateKeysListPage({super.key});
@override @override
_PrivateKeyListState createState() => _PrivateKeyListState(); State<PrivateKeysListPage> createState() => _PrivateKeyListState();
} }
class _PrivateKeyListState extends State<PrivateKeysListPage> class _PrivateKeyListState extends State<PrivateKeysListPage>

View File

@@ -16,7 +16,7 @@ class ProcessPage extends StatefulWidget {
const ProcessPage({super.key, required this.spi}); const ProcessPage({super.key, required this.spi});
@override @override
_ProcessPageState createState() => _ProcessPageState(); State<ProcessPage> createState() => _ProcessPageState();
} }
class _ProcessPageState extends State<ProcessPage> { class _ProcessPageState extends State<ProcessPage> {

View File

@@ -19,7 +19,7 @@ final class PvePage extends StatefulWidget {
}); });
@override @override
_PvePageState createState() => _PvePageState(); State<PvePage> createState() => _PvePageState();
} }
const _kHorziPadding = 11.0; const _kHorziPadding = 11.0;

View File

@@ -32,7 +32,7 @@ class ServerDetailPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
@override @override
_ServerDetailPageState createState() => _ServerDetailPageState(); State<ServerDetailPage> createState() => _ServerDetailPageState();
} }
class _ServerDetailPageState extends State<ServerDetailPage> class _ServerDetailPageState extends State<ServerDetailPage>

View File

@@ -20,7 +20,7 @@ class ServerEditPage extends StatefulWidget {
final ServerPrivateInfo? spi; final ServerPrivateInfo? spi;
@override @override
_ServerEditPageState createState() => _ServerEditPageState(); State<ServerEditPage> createState() => _ServerEditPageState();
} }
class _ServerEditPageState extends State<ServerEditPage> { class _ServerEditPageState extends State<ServerEditPage> {

View File

@@ -25,7 +25,7 @@ class ServerPage extends StatefulWidget {
const ServerPage({super.key}); const ServerPage({super.key});
@override @override
_ServerPageState createState() => _ServerPageState(); State<ServerPage> createState() => _ServerPageState();
} }
class _ServerPageState extends State<ServerPage> class _ServerPageState extends State<ServerPage>

View File

@@ -23,7 +23,7 @@ class SettingPage extends StatefulWidget {
const SettingPage({super.key}); const SettingPage({super.key});
@override @override
_SettingPageState createState() => _SettingPageState(); State<SettingPage> createState() => _SettingPageState();
} }
class _SettingPageState extends State<SettingPage> { class _SettingPageState extends State<SettingPage> {

View File

@@ -14,7 +14,7 @@ class IOSSettingsPage extends StatefulWidget {
const IOSSettingsPage({super.key}); const IOSSettingsPage({super.key});
@override @override
_IOSSettingsPageState createState() => _IOSSettingsPageState(); State<IOSSettingsPage> createState() => _IOSSettingsPageState();
} }
class _IOSSettingsPageState extends State<IOSSettingsPage> { class _IOSSettingsPageState extends State<IOSSettingsPage> {
@@ -83,7 +83,7 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
} }
Widget _buildWatchApp() { Widget _buildWatchApp() {
return FutureWidget<Map<String, dynamic>?>( return FutureWidget(
future: () async { future: () async {
if (!await wc.isPaired) { if (!await wc.isPaired) {
return null; return null;
@@ -107,6 +107,7 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
} }
return ListTile( return ListTile(
title: const Text('Watch app'), title: const Text('Watch app'),
subtitle: Text(l10n.edit, style: UIs.textGrey),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () async => _onTapWatchApp(ctx), onTap: () async => _onTapWatchApp(ctx),
); );

View File

@@ -8,7 +8,7 @@ class ServerOrderPage extends StatefulWidget {
const ServerOrderPage({super.key}); const ServerOrderPage({super.key});
@override @override
_ServerOrderPageState createState() => _ServerOrderPageState(); State<ServerOrderPage> createState() => _ServerOrderPageState();
} }
class _ServerOrderPageState extends State<ServerOrderPage> { class _ServerOrderPageState extends State<ServerOrderPage> {

View File

@@ -8,7 +8,7 @@ class SSHVirtKeySettingPage extends StatefulWidget {
const SSHVirtKeySettingPage({super.key}); const SSHVirtKeySettingPage({super.key});
@override @override
_SSHVirtKeySettingPageState createState() => _SSHVirtKeySettingPageState(); State<SSHVirtKeySettingPage> createState() => _SSHVirtKeySettingPageState();
} }
class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> { class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {

View File

@@ -13,7 +13,7 @@ class SnippetEditPage extends StatefulWidget {
final Snippet? snippet; final Snippet? snippet;
@override @override
_SnippetEditPageState createState() => _SnippetEditPageState(); State<SnippetEditPage> createState() => _SnippetEditPageState();
} }
class _SnippetEditPageState extends State<SnippetEditPage> class _SnippetEditPageState extends State<SnippetEditPage>

View File

@@ -12,7 +12,7 @@ class SnippetListPage extends StatefulWidget {
const SnippetListPage({super.key}); const SnippetListPage({super.key});
@override @override
_SnippetListPageState createState() => _SnippetListPageState(); State<SnippetListPage> createState() => _SnippetListPageState();
} }
class _SnippetListPageState extends State<SnippetListPage> { class _SnippetListPageState extends State<SnippetListPage> {

View File

@@ -30,7 +30,7 @@ class SSHPage extends StatefulWidget {
final String? initCmd; final String? initCmd;
final bool notFromTab; final bool notFromTab;
final Function()? onSessionEnd; final Function()? onSessionEnd;
final FocusNode? focus; final GlobalKey<TerminalViewState>? terminalKey;
const SSHPage({ const SSHPage({
super.key, super.key,
@@ -38,11 +38,11 @@ class SSHPage extends StatefulWidget {
this.initCmd, this.initCmd,
this.notFromTab = true, this.notFromTab = true,
this.onSessionEnd, this.onSessionEnd,
this.focus, this.terminalKey,
}); });
@override @override
_SSHPageState createState() => _SSHPageState(); State<SSHPage> createState() => _SSHPageState();
} }
const _horizonPadding = 7.0; const _horizonPadding = 7.0;
@@ -52,7 +52,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
late final _terminal = Terminal(inputHandler: _keyboard); late final _terminal = Terminal(inputHandler: _keyboard);
final TerminalController _terminalController = TerminalController(); final TerminalController _terminalController = TerminalController();
final List<List<VirtKey>> _virtKeysList = []; final List<List<VirtKey>> _virtKeysList = [];
late final _focus = widget.focus ?? FocusNode(); late final _termKey = widget.terminalKey ?? GlobalKey<TerminalViewState>();
late MediaQueryData _media; late MediaQueryData _media;
late TerminalStyle _terminalStyle; late TerminalStyle _terminalStyle;
@@ -144,8 +144,8 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
), ),
child: TerminalView( child: TerminalView(
_terminal, _terminal,
key: _termKey,
controller: _terminalController, controller: _terminalController,
focusNode: _focus,
keyboardType: TextInputType.text, keyboardType: TextInputType.text,
enableSuggestions: true, enableSuggestions: true,
textStyle: _terminalStyle, textStyle: _terminalStyle,
@@ -282,11 +282,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
Future<void> _doVirtualKeyFunc(VirtualKeyFunc type) async { Future<void> _doVirtualKeyFunc(VirtualKeyFunc type) async {
switch (type) { switch (type) {
case VirtualKeyFunc.toggleIME: case VirtualKeyFunc.toggleIME:
if (!_focus.hasFocus) { _termKey.currentState?.toggleFocus();
_focus.requestFocus();
} else {
_focus.unfocus();
}
break; break;
case VirtualKeyFunc.backspace: case VirtualKeyFunc.backspace:
_terminal.keyInput(TerminalKey.backspace); _terminal.keyInput(TerminalKey.backspace);

View File

@@ -17,8 +17,8 @@ class SSHTabPage extends StatefulWidget {
class _SSHTabPageState extends State<SSHTabPage> class _SSHTabPageState extends State<SSHTabPage>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
late final _tabMap = <String, ({Widget page, FocusNode? focus})>{ late final _tabMap = <String, ({Widget page})>{
l10n.add: (page: _buildAddPage(), focus: null), l10n.add: (page: _buildAddPage()),
}; };
late var _tabController = TabController( late var _tabController = TabController(
length: _tabMap.length, length: _tabMap.length,
@@ -38,8 +38,7 @@ class _SSHTabPageState extends State<SSHTabPage>
dividerColor: Colors.transparent, dividerColor: Colors.transparent,
onTap: (value) { onTap: (value) {
_fabRN.value = value; _fabRN.value = value;
final mapKey = _tabMap.keys.elementAt(value); FocusScope.of(context).unfocus();
_tabMap[mapKey]?.focus?.requestFocus();
}, },
), ),
body: _buildBody(), body: _buildBody(),
@@ -70,9 +69,12 @@ class _SSHTabPageState extends State<SSHTabPage>
IconBtn( IconBtn(
icon: Icons.close, icon: Icons.close,
onTap: () async { onTap: () async {
final confirm = await context.showRoundDialog<bool>( final confirm = await showDialog<bool>(
title: l10n.attention, context: context,
child: Text('${l10n.close} SSH ${l10n.conn}($e) ?'), builder: (context) {
return AlertDialog(
title: Text(l10n.attention),
content: Text('${l10n.close} SSH ${l10n.conn}($e) ?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => context.pop(true), onPressed: () => context.pop(true),
@@ -84,6 +86,10 @@ class _SSHTabPageState extends State<SSHTabPage>
), ),
], ],
); );
},
);
Future.delayed(const Duration(milliseconds: 50),
FocusScope.of(context).unfocus);
if (confirm != true) { if (confirm != true) {
return; return;
} }
@@ -142,18 +148,15 @@ class _SSHTabPageState extends State<SSHTabPage>
} }
return spi.name; return spi.name;
}(); }();
final focus = FocusNode();
_tabMap[name] = ( _tabMap[name] = (
page: SSHPage( page: SSHPage(
spi: spi, spi: spi,
focus: focus,
notFromTab: false, notFromTab: false,
onSessionEnd: () { onSessionEnd: () {
_tabMap.remove(name); _tabMap.remove(name);
_refreshTabs(); _refreshTabs();
}, },
), ),
focus: focus,
); );
_refreshTabs(); _refreshTabs();
final idx = _tabMap.length - 1; final idx = _tabMap.length - 1;

View File

@@ -32,7 +32,7 @@ class SftpPage extends StatefulWidget {
}); });
@override @override
_SftpPageState createState() => _SftpPageState(); State<SftpPage> createState() => _SftpPageState();
} }
class _SftpPageState extends State<SftpPage> with AfterLayoutMixin { class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {

View File

@@ -12,7 +12,7 @@ class SftpMissionPage extends StatefulWidget {
const SftpMissionPage({super.key}); const SftpMissionPage({super.key});
@override @override
_SftpMissionPageState createState() => _SftpMissionPageState(); State<SftpMissionPage> createState() => _SftpMissionPageState();
} }
class _SftpMissionPageState extends State<SftpMissionPage> { class _SftpMissionPageState extends State<SftpMissionPage> {

View File

@@ -1434,20 +1434,19 @@ packages:
watch_connectivity: watch_connectivity:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." name: watch_connectivity
ref: master sha256: a4257a314601c8448662d1a91421321a2e8a3207937fec4792116f1270ea8766
resolved-ref: ad5f151a5591b64d384ef22b7855b7a2a53ad3d3 url: "https://pub.dev"
url: "https://github.com/lollipopkit/watch_connectivity" source: hosted
source: git version: "0.2.0"
version: "0.1.5"
watch_connectivity_platform_interface: watch_connectivity_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: watch_connectivity_platform_interface name: watch_connectivity_platform_interface
sha256: "9074115391bd764c08a17346fcbc4d5c0b555672defbe6928ac648503b54aa9c" sha256: "82e8f165eac779d71bff7f6269a8be530798311cf7f3c33f1594d93468747d11"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.2" version: "0.2.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@@ -1525,8 +1524,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9 ref: master
resolved-ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9 resolved-ref: "13a280e77dd077b439af24ad3d054d318ae5df4a"
url: "https://github.com/lollipopkit/xterm.dart" url: "https://github.com/lollipopkit/xterm.dart"
source: git source: git
version: "4.0.0" version: "4.0.0"

View File

@@ -27,7 +27,7 @@ dependencies:
intl: ^0.19.0 intl: ^0.19.0
xterm: xterm:
git: git:
ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9 ref: master
url: https://github.com/lollipopkit/xterm.dart url: https://github.com/lollipopkit/xterm.dart
plain_notification_token: ^0.0.4 plain_notification_token: ^0.0.4
highlight: ^0.7.0 highlight: ^0.7.0
@@ -35,10 +35,7 @@ dependencies:
code_text_field: ^1.1.0 code_text_field: ^1.1.0
shared_preferences: ^2.1.1 shared_preferences: ^2.1.1
dynamic_color: ^1.6.6 dynamic_color: ^1.6.6
watch_connectivity: watch_connectivity: ^0.2.0
git:
ref: master
url: https://github.com/lollipopkit/watch_connectivity
#flutter_secure_storage: ^9.0.0 #flutter_secure_storage: ^9.0.0
xml: ^6.4.2 # for parsing nvidia-smi xml: ^6.4.2 # for parsing nvidia-smi
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
@@ -63,8 +60,8 @@ dependency_overrides:
# path: ../dartssh2 # path: ../dartssh2
# fl_lib: # fl_lib:
# path: ../fl_lib # path: ../fl_lib
# xterm: # xterm:
# path: ../xterm.dart # path: ../xterm.dart
dev_dependencies: dev_dependencies:
flutter_native_splash: ^2.1.6 flutter_native_splash: ^2.1.6