opt.: custom terminal emulator (#771)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-06-04 19:13:31 +08:00
committed by GitHub
parent 7127c960f7
commit f6d394c71e
30 changed files with 303 additions and 61 deletions

View File

@@ -9,6 +9,7 @@ extension _SSH on _AppSettingsPageState {
_buildTermTheme(),
_buildFont(),
_buildTermFontSize(),
if (isDesktop) _buildDesktopTerminal(),
_buildSSHVirtualKeyAutoOff(),
if (isMobile) _buildSSHVirtKeys(),
].map((e) => CardX(child: e)).toList(),
@@ -37,15 +38,10 @@ extension _SSH on _AppSettingsPageState {
return ListTile(
leading: const Icon(MingCute.font_fill),
title: Text(l10n.font),
trailing: _setting.fontPath.listenable().listenVal(
(val) {
final fontName = val.getFileName();
return Text(
fontName ?? libL10n.empty,
style: UIs.text15,
);
},
),
trailing: _setting.fontPath.listenable().listenVal((val) {
final fontName = val.getFileName();
return Text(fontName ?? libL10n.empty, style: UIs.text15);
}),
onTap: () {
context.showRoundDialog(
title: l10n.font,
@@ -61,7 +57,7 @@ extension _SSH on _AppSettingsPageState {
RNodes.app.notify();
},
child: Text(libL10n.clear),
)
),
],
);
},
@@ -85,6 +81,42 @@ extension _SSH on _AppSettingsPageState {
RNodes.app.notify();
}
Widget _buildDesktopTerminal() {
return _setting.desktopTerminal.listenable().listenVal((val) {
return ListTile(
leading: const Icon(Icons.terminal),
title: TipText(l10n.terminal, l10n.desktopTerminalTip),
trailing: Text(
val,
style: UIs.text15,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onTap: () async {
final ctrl = TextEditingController(text: val);
void onSave() {
_setting.desktopTerminal.put(ctrl.text.trim());
context.pop();
}
context.showRoundDialog<bool>(
title: libL10n.select,
child: Input(
controller: ctrl,
autoFocus: true,
label: l10n.terminal,
hint: 'x-terminal-emulator / gnome-terminal',
icon: Icons.edit,
suggestion: false,
onSubmitted: (_) => onSave(),
),
actions: Btn.ok(onTap: onSave).toList,
);
},
);
});
}
Widget _buildTermTheme() {
String index2Str(int index) {
switch (index) {
@@ -104,10 +136,7 @@ extension _SSH on _AppSettingsPageState {
title: Text(l10n.theme),
trailing: ValBuilder(
listenable: _setting.termTheme.listenable(),
builder: (val) => Text(
index2Str(val),
style: UIs.text15,
),
builder: (val) => Text(index2Str(val), style: UIs.text15),
),
onTap: () async {
final selected = await context.showPickSingleDialog(
@@ -140,7 +169,9 @@ extension _SSH on _AppSettingsPageState {
// style: UIs.textGrey,
// ),
title: TipText(
l10n.letterCache, '${l10n.letterCacheTip}\n${l10n.needRestart}'),
l10n.letterCache,
'${l10n.letterCacheTip}\n${l10n.needRestart}',
),
trailing: StoreSwitch(prop: _setting.letterCache),
);
}

View File

@@ -22,10 +22,7 @@ import 'package:server_box/view/page/systemd.dart';
class ServerFuncBtnsTopRight extends StatelessWidget {
final Spi spi;
const ServerFuncBtnsTopRight({
super.key,
required this.spi,
});
const ServerFuncBtnsTopRight({super.key, required this.spi});
@override
Widget build(BuildContext context) {
@@ -38,10 +35,7 @@ class ServerFuncBtnsTopRight extends StatelessWidget {
}
class ServerFuncBtns extends StatelessWidget {
const ServerFuncBtns({
super.key,
required this.spi,
});
const ServerFuncBtns({super.key, required this.spi});
final Spi spi;
@@ -86,7 +80,7 @@ class ServerFuncBtns extends StatelessWidget {
padding: EdgeInsets.zero,
icon: Icon(e.icon, size: 17),
),
Text(e.toStr, style: UIs.text11Grey)
Text(e.toStr, style: UIs.text11Grey),
],
),
);
@@ -107,11 +101,7 @@ class ServerFuncBtns extends StatelessWidget {
}
}
void _onTapMoreBtns(
ServerFuncBtn value,
Spi spi,
BuildContext context,
) async {
void _onTapMoreBtns(ServerFuncBtn value, Spi spi, BuildContext context) async {
// final isMobile = ResponsiveBreakpoints.of(context).isMobile;
switch (value) {
// case ServerFuncBtn.pkg:
@@ -153,16 +143,8 @@ void _onTapMoreBtns(
final fmted = snippet.fmtWithSpi(spi);
final sure = await context.showRoundDialog<bool>(
title: libL10n.attention,
child: SingleChildScrollView(
child: SimpleMarkdown(data: '```shell\n$fmted\n```'),
),
actions: [
CountDownBtn(
onTap: () => context.pop(true),
text: l10n.run,
afterColor: Colors.red,
),
],
child: SingleChildScrollView(child: SimpleMarkdown(data: '```shell\n$fmted\n```')),
actions: [CountDownBtn(onTap: () => context.pop(true), text: l10n.run, afterColor: Colors.red)],
);
if (sure != true) return;
if (!_checkClient(context, spi.id)) return;
@@ -178,13 +160,13 @@ void _onTapMoreBtns(
case ServerFuncBtn.container:
if (!_checkClient(context, spi.id)) return;
final args = SpiRequiredArgs(spi);
if (isMobile) {
ContainerPage.route.go(context, args);
} else {
SplitViewNavigator.of(context)?.replace(
ContainerPage.route.toWidget(args: args),
);
}
// if (isMobile) {
ContainerPage.route.go(context, args);
// } else {
// SplitViewNavigator.of(
// context,
// )?.replace(ContainerPage.route.toWidget(args: args));
// }
break;
case ServerFuncBtn.process:
if (!_checkClient(context, spi.id)) return;
@@ -260,7 +242,23 @@ void _gotoSSH(Spi spi, BuildContext context) async {
await Process.start('cmd', ['/c', 'start'] + sshCommand);
break;
case Pfs.linux:
await Process.start('x-terminal-emulator', ['-e'] + sshCommand);
final scriptFile = File('${Directory.systemTemp.path}/srvbox_launch_term.sh');
await scriptFile.writeAsString(_runEmulatorShell);
if (Platform.isLinux || Platform.isMacOS) {
await Process.run('chmod', ['+x', scriptFile.path]);
}
try {
var terminal = Stores.setting.desktopTerminal.fetch();
if (terminal.isEmpty) terminal = 'x-terminal-emulator';
await Process.start(scriptFile.path, [terminal, ...sshCommand]);
} catch (e, s) {
context.showErrDialog(e, s, l10n.emulator);
} finally {
await scriptFile.delete();
}
break;
default:
context.showSnackBar('Mismatch system: $system');
@@ -280,3 +278,62 @@ bool _checkClient(BuildContext context, String id) {
}
return true;
}
const _runEmulatorShell = '''
#!/bin/sh
# launch_terminal.sh
TERMINAL="\$1"
shift # Remove the first argument (terminal name)
# Auto detect terminal if not provided
if [ -z "\$TERMINAL" ] || [ "\$TERMINAL" = "x-terminal-emulator" ]; then
# Follow the order of preference
for term in kitty alacritty gnome-terminal konsole xfce4-terminal terminator tilix wezterm foot; do
if command -v "\$term" >/dev/null 2>&1; then
TERMINAL="\$term"
break
fi
done
[ -z "\$TERMINAL" ] && TERMINAL="x-terminal-emulator"
fi
case "\$TERMINAL" in
gnome-terminal)
exec "\$TERMINAL" -- "\$@"
;;
konsole|terminator|tilix)
exec "\$TERMINAL" -e "\$@"
;;
xfce4-terminal)
exec "\$TERMINAL" -e "\$*"
;;
alacritty)
# Check alacritty version
if "\$TERMINAL" --version 2>&1 | grep -q "alacritty 0\\.1[3-9]"; then
# 0.13.0+
exec "\$TERMINAL" --command "\$@"
else
# Old versions
exec "\$TERMINAL" -e "\$@"
fi
;;
kitty)
exec "\$TERMINAL" "\$@"
;;
wezterm)
exec "\$TERMINAL" start -- "\$@"
;;
foot)
exec "\$TERMINAL" "\$@"
;;
urxvt|rxvt-unicode)
exec "\$TERMINAL" -e "\$@"
;;
x-terminal-emulator|*)
# Default
exec "\$TERMINAL" -e "\$@"
;;
esac
''';