mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-15 12:44:59 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5e22cbc65 | ||
|
|
7926a4d4a6 | ||
|
|
39a3e0800b | ||
|
|
cd3c094af0 | ||
|
|
8589b3b4d7 | ||
|
|
7693e30cbf |
149
.github/workflows/release.yml
vendored
149
.github/workflows/release.yml
vendored
@@ -9,10 +9,9 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
# Set by fl_build
|
||||
# env:
|
||||
# APP_NAME: ServerBox
|
||||
# BUILD_NUMBER: ${{ github.ref_name }}
|
||||
env:
|
||||
APP_NAME: ServerBox
|
||||
RELEASE_TAG: ${{ github.ref_name }}
|
||||
|
||||
jobs:
|
||||
releaseAndroid:
|
||||
@@ -21,11 +20,13 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.38.0"
|
||||
flutter-version: "3.38.7"
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "zulu"
|
||||
@@ -37,17 +38,28 @@ jobs:
|
||||
- name: Build
|
||||
run: dart run fl_build -p android
|
||||
- name: Rename for fdroid
|
||||
shell: bash
|
||||
run: |
|
||||
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm64.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm64.apk
|
||||
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm.apk
|
||||
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_amd64.apk
|
||||
APK_DIR="build/app/outputs/flutter-apk"
|
||||
shopt -s nullglob
|
||||
|
||||
for arch in arm64 arm amd64; do
|
||||
matches=("$APK_DIR"/"${APP_NAME}"_*_"${arch}".apk)
|
||||
if [ ${#matches[@]} -ne 1 ]; then
|
||||
echo "Error: expected 1 APK for ${arch}, found ${#matches[@]}"
|
||||
echo "APK_DIR: $APK_DIR"
|
||||
ls -la "$APK_DIR" || true
|
||||
exit 1
|
||||
fi
|
||||
mv "${matches[0]}" "$APK_DIR/${APP_NAME}_${RELEASE_TAG}_${arch}.apk"
|
||||
done
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm64.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_amd64.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_arm64.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_arm.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_amd64.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -57,6 +69,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- name: Install dependencies
|
||||
@@ -69,11 +83,22 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
dart run fl_build -p linux
|
||||
- name: Rename for release
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
matches=("${APP_NAME}"_*_amd64.AppImage)
|
||||
if [ ${#matches[@]} -ne 1 ]; then
|
||||
echo "Error: expected 1 AppImage, found ${#matches[@]}"
|
||||
ls -la || true
|
||||
exit 1
|
||||
fi
|
||||
mv "${matches[0]}" "${APP_NAME}_${RELEASE_TAG}_amd64.AppImage"
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.AppImage
|
||||
${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_amd64.AppImage
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -83,32 +108,96 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- name: Build
|
||||
run: dart run fl_build -p windows
|
||||
- name: Rename for release
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
matches=("${APP_NAME}"_*_windows_amd64.zip)
|
||||
if [ ${#matches[@]} -ne 1 ]; then
|
||||
echo "Error: expected 1 zip, found ${#matches[@]}"
|
||||
ls -la || true
|
||||
exit 1
|
||||
fi
|
||||
mv "${matches[0]}" "${APP_NAME}_${RELEASE_TAG}_windows_amd64.zip"
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_windows_amd64.zip
|
||||
${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_windows_amd64.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# releaseApple:
|
||||
# name: Release ios and macos
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v6
|
||||
# - name: Install Flutter
|
||||
# uses: subosito/flutter-action@v2
|
||||
# - name: Build
|
||||
# run: dart run fl_build -p ios
|
||||
# - name: Create Release
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ env.APP_NAME }}_universal.ipa
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
releaseIOS:
|
||||
name: Release iOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- name: Build
|
||||
run: |
|
||||
dart run fl_build -p ios -- --no-codesign
|
||||
shopt -s nullglob
|
||||
IPA_FILES=(build/ios/ipa/*.ipa)
|
||||
if [ ${#IPA_FILES[@]} -ne 1 ]; then
|
||||
echo "Error: expected 1 IPA, found ${#IPA_FILES[@]}"
|
||||
ls -la build/ios/ipa || true
|
||||
exit 1
|
||||
fi
|
||||
IPA_FILE="${IPA_FILES[0]}"
|
||||
echo "Found IPA: $IPA_FILE"
|
||||
cp "$IPA_FILE" "${APP_NAME}_${RELEASE_TAG}_ios.ipa"
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_ios.ipa
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
releaseMacOS:
|
||||
name: Release macOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- name: Build
|
||||
run: |
|
||||
dart run fl_build -p macos -- --no-codesign
|
||||
- name: Package
|
||||
run: |
|
||||
RELEASE_DIR="$GITHUB_WORKSPACE/build/macos/Build/Products/Release"
|
||||
APP_DIR="$RELEASE_DIR/$APP_NAME.app"
|
||||
OUT_ZIP="$GITHUB_WORKSPACE/${APP_NAME}_${RELEASE_TAG}_macos.zip"
|
||||
|
||||
if [ ! -d "$RELEASE_DIR" ]; then
|
||||
echo "Error: macOS release directory not found: $RELEASE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$APP_DIR" ]; then
|
||||
echo "Error: macOS app bundle not found: $APP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$RELEASE_DIR"
|
||||
zip -ry "$OUT_ZIP" "$APP_NAME.app"
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ env.APP_NAME }}_${{ env.RELEASE_TAG }}_macos.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -748,7 +748,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -758,7 +758,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -884,7 +884,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -894,7 +894,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -912,7 +912,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -922,7 +922,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -943,7 +943,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -956,7 +956,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
@@ -982,7 +982,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -995,7 +995,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1018,7 +1018,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1031,7 +1031,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1054,7 +1054,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1066,7 +1066,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
@@ -1095,7 +1095,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1107,7 +1107,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
@@ -1133,7 +1133,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1145,7 +1145,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
|
||||
@@ -112,7 +112,7 @@ extension SSHClientX on SSHClient {
|
||||
return (session, result.takeBytes().string);
|
||||
}
|
||||
|
||||
Future<int?> execWithPwd(
|
||||
Future<(int?, String)> execWithPwd(
|
||||
String script, {
|
||||
String? entry,
|
||||
BuildContext? context,
|
||||
@@ -121,7 +121,7 @@ extension SSHClientX on SSHClient {
|
||||
required String id,
|
||||
}) async {
|
||||
var isRequestingPwd = false;
|
||||
final (session, _) = await exec(
|
||||
final (session, output) = await exec(
|
||||
(sess) {
|
||||
sess.stdin.add('$script\n'.uint8List);
|
||||
sess.stdin.close();
|
||||
@@ -147,7 +147,7 @@ extension SSHClientX on SSHClient {
|
||||
onStdout: onStdout,
|
||||
entry: entry,
|
||||
);
|
||||
return session.exitCode;
|
||||
return (session.exitCode, output);
|
||||
}
|
||||
|
||||
Future<String> execForOutput(
|
||||
|
||||
@@ -26,6 +26,7 @@ enum ContainerErrType {
|
||||
parsePs,
|
||||
parseImages,
|
||||
parseStats,
|
||||
podmanDetected,
|
||||
}
|
||||
|
||||
class ContainerErr extends Err<ContainerErrType> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:server_box/core/extension/context/locale.dart';
|
||||
import 'package:server_box/core/extension/ssh_client.dart';
|
||||
import 'package:server_box/data/model/app/error.dart';
|
||||
import 'package:server_box/data/model/app/scripts/script_consts.dart';
|
||||
@@ -18,6 +19,7 @@ part 'container.freezed.dart';
|
||||
part 'container.g.dart';
|
||||
|
||||
final _dockerNotFound = RegExp(r"command not found|Unknown command|Command '\w+' not found");
|
||||
final _podmanEmulationMsg = 'Emulate Docker CLI using podman';
|
||||
|
||||
@freezed
|
||||
abstract class ContainerState with _$ContainerState {
|
||||
@@ -84,21 +86,51 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
}
|
||||
final includeStats = Stores.setting.containerParseStat.fetch();
|
||||
|
||||
var raw = '';
|
||||
final cmd = _wrap(ContainerCmdType.execAll(state.type, sudo: sudo, includeStats: includeStats));
|
||||
final code = await client?.execWithPwd(
|
||||
cmd,
|
||||
context: context,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
id: hostId,
|
||||
);
|
||||
int? code;
|
||||
String raw = '';
|
||||
final errs = <String>[];
|
||||
if (client != null) {
|
||||
(code, raw) = await client!.execWithPwd(cmd, context: context, id: hostId);
|
||||
} else {
|
||||
state = state.copyWith(
|
||||
isBusy: false,
|
||||
error: ContainerErr(type: ContainerErrType.noClient),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isBusy: false);
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
/// Code 127 means command not found
|
||||
if (code == 127 || raw.contains(_dockerNotFound)) {
|
||||
if (code == 127 || raw.contains(_dockerNotFound) || errs.join().contains(_dockerNotFound)) {
|
||||
state = state.copyWith(error: ContainerErr(type: ContainerErrType.notInstalled));
|
||||
return;
|
||||
}
|
||||
|
||||
/// Pre-parse Podman detection
|
||||
if (raw.contains(_podmanEmulationMsg)) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(
|
||||
type: ContainerErrType.podmanDetected,
|
||||
message: l10n.podmanDockerEmulationDetected,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Filter out sudo password prompt from output
|
||||
if (errs.any((e) => e.contains('[sudo] password'))) {
|
||||
raw = raw.split('\n').where((line) => !line.contains('[sudo] password')).join('\n');
|
||||
}
|
||||
|
||||
/// Detect Podman not installed when using Podman mode
|
||||
if (state.type == ContainerType.podman &&
|
||||
(errs.any((e) => e.contains('podman: not found')) ||
|
||||
raw.contains('podman: not found'))) {
|
||||
state = state.copyWith(error: ContainerErr(type: ContainerErrType.notInstalled));
|
||||
return;
|
||||
}
|
||||
@@ -122,9 +154,11 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
final version = json.decode(verRaw)['Client']['Version'];
|
||||
state = state.copyWith(version: version, error: null);
|
||||
} catch (e, trace) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.invalidVersion, message: '$e'),
|
||||
);
|
||||
if (state.error == null) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.invalidVersion, message: '$e'),
|
||||
);
|
||||
}
|
||||
Loggers.app.warning('Container version failed', e, trace);
|
||||
}
|
||||
|
||||
@@ -140,9 +174,11 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
final items = lines.map((e) => ContainerPs.fromRaw(e, state.type)).toList();
|
||||
state = state.copyWith(items: items);
|
||||
} catch (e, trace) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parsePs, message: '$e'),
|
||||
);
|
||||
if (state.error == null) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parsePs, message: '$e'),
|
||||
);
|
||||
}
|
||||
Loggers.app.warning('Container ps failed', e, trace);
|
||||
}
|
||||
|
||||
@@ -162,9 +198,11 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
}
|
||||
state = state.copyWith(images: images);
|
||||
} catch (e, trace) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parseImages, message: '$e'),
|
||||
);
|
||||
if (state.error == null) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parseImages, message: '$e'),
|
||||
);
|
||||
}
|
||||
Loggers.app.warning('Container images failed', e, trace);
|
||||
}
|
||||
|
||||
@@ -189,9 +227,11 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
item.parseStats(statsLine, state.version);
|
||||
}
|
||||
} catch (e, trace) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parseStats, message: '$e'),
|
||||
);
|
||||
if (state.error == null) {
|
||||
state = state.copyWith(
|
||||
error: ContainerErr(type: ContainerErrType.parseStats, message: '$e'),
|
||||
);
|
||||
}
|
||||
Loggers.app.warning('Parse docker stats: $statsRaw', e, trace);
|
||||
}
|
||||
}
|
||||
@@ -227,6 +267,10 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
}
|
||||
|
||||
Future<ContainerErr?> run(String cmd, {bool autoRefresh = true}) async {
|
||||
if (client == null) {
|
||||
return ContainerErr(type: ContainerErrType.noClient);
|
||||
}
|
||||
|
||||
cmd = switch (state.type) {
|
||||
ContainerType.docker => 'docker $cmd',
|
||||
ContainerType.podman => 'podman $cmd',
|
||||
@@ -234,7 +278,7 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
|
||||
state = state.copyWith(runLog: '');
|
||||
final errs = <String>[];
|
||||
final code = await client?.execWithPwd(
|
||||
final (code, _) = await client?.execWithPwd(
|
||||
_wrap((await sudoCompleter.future) ? 'sudo -S $cmd' : cmd),
|
||||
context: context,
|
||||
onStdout: (data, _) {
|
||||
@@ -242,7 +286,7 @@ class ContainerNotifier extends _$ContainerNotifier {
|
||||
},
|
||||
onStderr: (data, _) => errs.add(data),
|
||||
id: hostId,
|
||||
);
|
||||
) ?? (null, null);
|
||||
state = state.copyWith(runLog: null);
|
||||
|
||||
if (code != 0) {
|
||||
|
||||
@@ -58,7 +58,7 @@ final class ContainerNotifierProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$containerNotifierHash() => r'fea65e66499234b0a59bffff8d69c4ab8c93b2fd';
|
||||
String _$containerNotifierHash() => r'85457ec75264199c284572ee45beeaccba2044a1';
|
||||
|
||||
final class ContainerNotifierFamily extends $Family
|
||||
with
|
||||
|
||||
@@ -58,7 +58,7 @@ final class PveNotifierProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$pveNotifierHash() => r'ba5f2d6cb47c33735f7cc09b771b4a86501b86c6';
|
||||
String _$pveNotifierHash() => r'1e71faadee074b9c07bee731ef4ae6505e791967';
|
||||
|
||||
final class PveNotifierFamily extends $Family
|
||||
with $ClassFamilyOverride<PveNotifier, PveState, PveState, PveState, Spi> {
|
||||
|
||||
@@ -103,37 +103,44 @@ class ServersNotifier extends _$ServersNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
await Future.wait(
|
||||
state.servers.entries.map((entry) async {
|
||||
final serverId = entry.key;
|
||||
final spi = entry.value;
|
||||
final serversToRefresh = <MapEntry<String, Spi>>[];
|
||||
final idsToResetLimiter = <String>[];
|
||||
|
||||
if (onlyFailed) {
|
||||
final serverState = ref.read(serverProvider(serverId));
|
||||
if (serverState.conn != ServerConn.failed) return;
|
||||
TryLimiter.reset(serverId);
|
||||
}
|
||||
for (final entry in state.servers.entries) {
|
||||
final serverId = entry.key;
|
||||
final spi = entry.value;
|
||||
|
||||
if (state.manualDisconnectedIds.contains(serverId)) return;
|
||||
if (state.manualDisconnectedIds.contains(serverId)) continue;
|
||||
|
||||
final serverState = ref.read(serverProvider(serverId));
|
||||
if (serverState.conn == ServerConn.disconnected && !spi.autoConnect) {
|
||||
return;
|
||||
}
|
||||
final serverState = ref.read(serverProvider(serverId));
|
||||
|
||||
final serverNotifier = ref.read(serverProvider(serverId).notifier);
|
||||
await serverNotifier.refresh();
|
||||
}),
|
||||
);
|
||||
if (onlyFailed) {
|
||||
if (serverState.conn != ServerConn.failed) continue;
|
||||
idsToResetLimiter.add(serverId);
|
||||
}
|
||||
|
||||
if (serverState.conn == ServerConn.disconnected && !spi.autoConnect) continue;
|
||||
|
||||
serversToRefresh.add(entry);
|
||||
}
|
||||
|
||||
for (final id in idsToResetLimiter) {
|
||||
TryLimiter.reset(id);
|
||||
}
|
||||
|
||||
for (final entry in serversToRefresh) {
|
||||
final serverNotifier = ref.read(serverProvider(entry.key).notifier);
|
||||
serverNotifier.refresh().ignore();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startAutoRefresh() async {
|
||||
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
|
||||
stopAutoRefresh();
|
||||
if (duration == 0) return;
|
||||
if (duration < 0 || duration > 10 || duration == 1) {
|
||||
duration = 3;
|
||||
if (duration <= 1 || duration > 10) {
|
||||
Loggers.app.warning('Invalid duration: $duration, use default 3');
|
||||
duration = 3;
|
||||
}
|
||||
final timer = Timer.periodic(Duration(seconds: duration), (_) async {
|
||||
await refresh();
|
||||
@@ -145,8 +152,8 @@ class ServersNotifier extends _$ServersNotifier {
|
||||
final timer = state.autoRefreshTimer;
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
state = state.copyWith(autoRefreshTimer: null);
|
||||
}
|
||||
state = state.copyWith(autoRefreshTimer: null);
|
||||
}
|
||||
|
||||
bool get isAutoRefreshOn => state.autoRefreshTimer != null;
|
||||
|
||||
@@ -41,7 +41,7 @@ final class ServersNotifierProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$serversNotifierHash() => r'3292bdce7d602ff64687b05ff81d120e71761ec2';
|
||||
String _$serversNotifierHash() => r'dc5da44f9bd8d8dcfba3e6e932cca3e2f379e582';
|
||||
|
||||
abstract class _$ServersNotifier extends $Notifier<ServersState> {
|
||||
ServersState build();
|
||||
|
||||
@@ -35,7 +35,6 @@ abstract class ServerState with _$ServerState {
|
||||
required ServerStatus status,
|
||||
@Default(ServerConn.disconnected) ServerConn conn,
|
||||
SSHClient? client,
|
||||
Future<void>? updateFuture,
|
||||
}) = _ServerState;
|
||||
}
|
||||
|
||||
@@ -81,19 +80,16 @@ class ServerNotifier extends _$ServerNotifier {
|
||||
}
|
||||
|
||||
// Refresh server status
|
||||
bool _isRefreshing = false;
|
||||
|
||||
Future<void> refresh() async {
|
||||
if (state.updateFuture != null) {
|
||||
await state.updateFuture;
|
||||
return;
|
||||
}
|
||||
|
||||
final updateFuture = _updateServer();
|
||||
state = state.copyWith(updateFuture: updateFuture);
|
||||
if (_isRefreshing) return;
|
||||
|
||||
_isRefreshing = true;
|
||||
try {
|
||||
await updateFuture;
|
||||
await _updateServer();
|
||||
} finally {
|
||||
state = state.copyWith(updateFuture: null);
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$ServerState {
|
||||
|
||||
Spi get spi; ServerStatus get status; ServerConn get conn; SSHClient? get client; Future<void>? get updateFuture;
|
||||
Spi get spi; ServerStatus get status; ServerConn get conn; SSHClient? get client;
|
||||
/// Create a copy of ServerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $ServerStateCopyWith<ServerState> get copyWith => _$ServerStateCopyWithImpl<Serv
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
|
||||
int get hashCode => Object.hash(runtimeType,spi,status,conn,client);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
|
||||
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $ServerStateCopyWith<$Res> {
|
||||
factory $ServerStateCopyWith(ServerState value, $Res Function(ServerState) _then) = _$ServerStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
|
||||
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client
|
||||
});
|
||||
|
||||
|
||||
@@ -62,14 +62,13 @@ class _$ServerStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ServerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
|
||||
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
||||
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
|
||||
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
|
||||
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
|
||||
as Future<void>?,
|
||||
as SSHClient?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of ServerState
|
||||
@@ -163,10 +162,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ServerState() when $default != null:
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -184,10 +183,10 @@ return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFutur
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ServerState():
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -204,10 +203,10 @@ return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFutur
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ServerState() when $default != null:
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
|
||||
return $default(_that.spi,_that.status,_that.conn,_that.client);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -219,14 +218,13 @@ return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFutur
|
||||
|
||||
|
||||
class _ServerState implements ServerState {
|
||||
const _ServerState({required this.spi, required this.status, this.conn = ServerConn.disconnected, this.client, this.updateFuture});
|
||||
const _ServerState({required this.spi, required this.status, this.conn = ServerConn.disconnected, this.client});
|
||||
|
||||
|
||||
@override final Spi spi;
|
||||
@override final ServerStatus status;
|
||||
@override@JsonKey() final ServerConn conn;
|
||||
@override final SSHClient? client;
|
||||
@override final Future<void>? updateFuture;
|
||||
|
||||
/// Create a copy of ServerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -238,16 +236,16 @@ _$ServerStateCopyWith<_ServerState> get copyWith => __$ServerStateCopyWithImpl<_
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
|
||||
int get hashCode => Object.hash(runtimeType,spi,status,conn,client);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
|
||||
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client)';
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +256,7 @@ abstract mixin class _$ServerStateCopyWith<$Res> implements $ServerStateCopyWith
|
||||
factory _$ServerStateCopyWith(_ServerState value, $Res Function(_ServerState) _then) = __$ServerStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
|
||||
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client
|
||||
});
|
||||
|
||||
|
||||
@@ -275,14 +273,13 @@ class __$ServerStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ServerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,}) {
|
||||
return _then(_ServerState(
|
||||
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
|
||||
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
||||
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
|
||||
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
|
||||
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
|
||||
as Future<void>?,
|
||||
as SSHClient?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ final class ServerNotifierProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$serverNotifierHash() => r'185c6b4546c3bc526f5b2ca79d16aed665818863';
|
||||
String _$serverNotifierHash() => r'04b1beef4d96242fd10d5b523c6f5f17eb774bae';
|
||||
|
||||
final class ServerNotifierFamily extends $Family
|
||||
with
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
abstract class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 1291;
|
||||
static const int build = 1297;
|
||||
static const int script = 70;
|
||||
}
|
||||
|
||||
@@ -1933,6 +1933,12 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'Logs'**
|
||||
String get logs;
|
||||
|
||||
/// No description provided for @podmanDockerEmulationDetected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Podman Docker emulation detected. Please switch to Podman in settings.'**
|
||||
String get podmanDockerEmulationDetected;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -1031,4 +1031,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Protokolle';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Podman Docker-Emulation erkannt. Bitte wechseln Sie in den Einstellungen zu Podman.';
|
||||
}
|
||||
|
||||
@@ -1022,4 +1022,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Logs';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Podman Docker emulation detected. Please switch to Podman in settings.';
|
||||
}
|
||||
|
||||
@@ -1033,4 +1033,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Registros';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Detectada emulación de Podman Docker. Por favor, cambie a Podman en la configuración.';
|
||||
}
|
||||
|
||||
@@ -1036,4 +1036,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Journaux';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Émulation Podman Docker détectée. Veuillez passer à Podman dans les paramètres.';
|
||||
}
|
||||
|
||||
@@ -1022,4 +1022,8 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Log';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Emulasi Podman Docker terdeteksi. Silakan beralih ke Podman di pengaturan.';
|
||||
}
|
||||
|
||||
@@ -992,4 +992,8 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'ログ';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Podman Docker エミュレーションが検出されました。設定で Podman に切り替えてください。';
|
||||
}
|
||||
|
||||
@@ -1029,4 +1029,8 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Logboeken';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Podman Docker-emulatie gedetecteerd. Schakel over naar Podman in de instellingen.';
|
||||
}
|
||||
|
||||
@@ -1024,4 +1024,8 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Logs';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Emulação Podman Docker detectada. Por favor, alterne para Podman nas configurações.';
|
||||
}
|
||||
|
||||
@@ -1028,4 +1028,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Журналы';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Обнаружена эмуляция Podman Docker. Пожалуйста, переключитесь на Podman в настройках.';
|
||||
}
|
||||
|
||||
@@ -1023,4 +1023,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Günlükler';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Podman Docker emülasyonu tespit edildi. Lütfen ayarlarda Podman\'a geçin.';
|
||||
}
|
||||
|
||||
@@ -1028,4 +1028,8 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => 'Журнали';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'Виявлено емуляцію Podman Docker. Будь ласка, переключіться на Podman у налаштуваннях.';
|
||||
}
|
||||
|
||||
@@ -977,6 +977,10 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get logs => '日志';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'检测到 Podman Docker 仿真。请在设置中切换到 Podman。';
|
||||
}
|
||||
|
||||
/// The translations for Chinese, as used in Taiwan (`zh_TW`).
|
||||
@@ -1931,4 +1935,8 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
||||
|
||||
@override
|
||||
String get logs => '日誌';
|
||||
|
||||
@override
|
||||
String get podmanDockerEmulationDetected =>
|
||||
'檢測到 Podman Docker 仿真。請在設定中切換到 Podman。';
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Schreiben",
|
||||
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht.",
|
||||
"writeScriptTip": "Nach der Verbindung mit dem Server wird ein Skript in `~/.config/server_box` \n | `/tmp/server_box` geschrieben, um den Systemstatus zu überwachen. Sie können den Skriptinhalt überprüfen.",
|
||||
"logs": "Protokolle"
|
||||
"logs": "Protokolle",
|
||||
"podmanDockerEmulationDetected": "Podman Docker-Emulation erkannt. Bitte wechseln Sie in den Einstellungen zu Podman."
|
||||
}
|
||||
|
||||
@@ -304,5 +304,6 @@
|
||||
"menuGitHubRepository": "GitHub Repository",
|
||||
"menuWiki": "Wiki",
|
||||
"menuHelp": "Help",
|
||||
"logs": "Logs"
|
||||
"logs": "Logs",
|
||||
"podmanDockerEmulationDetected": "Podman Docker emulation detected. Please switch to Podman in settings."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Escribir",
|
||||
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe.",
|
||||
"writeScriptTip": "Después de conectarse al servidor, se escribirá un script en `~/.config/server_box` \n | `/tmp/server_box` para monitorear el estado del sistema. Puedes revisar el contenido del script.",
|
||||
"logs": "Registros"
|
||||
"logs": "Registros",
|
||||
"podmanDockerEmulationDetected": "Detectada emulación de Podman Docker. Por favor, cambie a Podman en la configuración."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Écrire",
|
||||
"writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas.",
|
||||
"writeScriptTip": "Après la connexion au serveur, un script sera écrit dans `~/.config/server_box` \n | `/tmp/server_box` pour surveiller l'état du système. Vous pouvez examiner le contenu du script.",
|
||||
"logs": "Journaux"
|
||||
"logs": "Journaux",
|
||||
"podmanDockerEmulationDetected": "Émulation Podman Docker détectée. Veuillez passer à Podman dans les paramètres."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Tulis",
|
||||
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada.",
|
||||
"writeScriptTip": "Setelah terhubung ke server, sebuah skrip akan ditulis ke `~/.config/server_box` \n | `/tmp/server_box` untuk memantau status sistem. Anda dapat meninjau konten skrip tersebut.",
|
||||
"logs": "Log"
|
||||
"logs": "Log",
|
||||
"podmanDockerEmulationDetected": "Emulasi Podman Docker terdeteksi. Silakan beralih ke Podman di pengaturan."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "書き込み",
|
||||
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。",
|
||||
"writeScriptTip": "サーバーへの接続後、システムステータスを監視するスクリプトが `~/.config/server_box` \n | `/tmp/server_box` に書き込まれます。スクリプトの内容を確認できます。",
|
||||
"logs": "ログ"
|
||||
"logs": "ログ",
|
||||
"podmanDockerEmulationDetected": "Podman Docker エミュレーションが検出されました。設定で Podman に切り替えてください。"
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Schrijven",
|
||||
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat.",
|
||||
"writeScriptTip": "Na het verbinden met de server wordt een script geschreven naar `~/.config/server_box` \n | `/tmp/server_box` om de systeemstatus te monitoren. U kunt de inhoud van het script controleren.",
|
||||
"logs": "Logboeken"
|
||||
"logs": "Logboeken",
|
||||
"podmanDockerEmulationDetected": "Podman Docker-emulatie gedetecteerd. Schakel over naar Podman in de instellingen."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Escrita",
|
||||
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe.",
|
||||
"writeScriptTip": "Após conectar ao servidor, um script será escrito em `~/.config/server_box` \n | `/tmp/server_box` para monitorar o status do sistema. Você pode revisar o conteúdo do script.",
|
||||
"logs": "Logs"
|
||||
"logs": "Logs",
|
||||
"podmanDockerEmulationDetected": "Emulação Podman Docker detectada. Por favor, alterne para Podman nas configurações."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Запись",
|
||||
"writeScriptFailTip": "Запись скрипта не удалась, возможно, из-за отсутствия прав или потому что, директории не существует.",
|
||||
"writeScriptTip": "После подключения к серверу скрипт будет записан в `~/.config/server_box` \n | `/tmp/server_box` для мониторинга состояния системы. Вы можете проверить содержимое скрипта.",
|
||||
"logs": "Журналы"
|
||||
"logs": "Журналы",
|
||||
"podmanDockerEmulationDetected": "Обнаружена эмуляция Podman Docker. Пожалуйста, переключитесь на Podman в настройках."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Yaz",
|
||||
"writeScriptFailTip": "Betik yazma başarısız oldu, muhtemelen izin eksikliği veya dizin mevcut değil.",
|
||||
"writeScriptTip": "Sunucuya bağlandıktan sonra, sistem durumunu izlemek için `~/.config/server_box` \n | `/tmp/server_box` dizinine bir betik yazılacak. Betik içeriğini inceleyebilirsiniz.",
|
||||
"logs": "Günlükler"
|
||||
"logs": "Günlükler",
|
||||
"podmanDockerEmulationDetected": "Podman Docker emülasyonu tespit edildi. Lütfen ayarlarda Podman'a geçin."
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "Записати",
|
||||
"writeScriptFailTip": "Запис у скрипт не вдався, можливо, через брак дозволів або каталог не існує.",
|
||||
"writeScriptTip": "Після підключення до сервера скрипт буде записано у `~/.config/server_box` \n | `/tmp/server_box` для моніторингу стану системи. Ви можете переглянути вміст скрипта.",
|
||||
"logs": "Журнали"
|
||||
"logs": "Журнали",
|
||||
"podmanDockerEmulationDetected": "Виявлено емуляцію Podman Docker. Будь ласка, переключіться на Podman у налаштуваннях."
|
||||
}
|
||||
|
||||
@@ -301,5 +301,6 @@
|
||||
"menuGitHubRepository": "GitHub 仓库",
|
||||
"menuWiki": "Wiki",
|
||||
"menuHelp": "帮助",
|
||||
"logs": "日志"
|
||||
"logs": "日志",
|
||||
"podmanDockerEmulationDetected": "检测到 Podman Docker 仿真。请在设置中切换到 Podman。"
|
||||
}
|
||||
|
||||
@@ -294,5 +294,6 @@
|
||||
"write": "寫入",
|
||||
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。",
|
||||
"writeScriptTip": "連線到伺服器後,將會在 `~/.config/server_box` \n | `/tmp/server_box` 中寫入一個腳本來監測系統狀態。你可以審查腳本內容。",
|
||||
"logs": "日誌"
|
||||
"logs": "日誌",
|
||||
"podmanDockerEmulationDetected": "檢測到 Podman Docker 仿真。請在設定中切換到 Podman。"
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ class _ContainerPageState extends ConsumerState<ContainerPage> {
|
||||
if (item.cpu == null || item.mem == null) return UIs.placeholder;
|
||||
return LayoutBuilder(
|
||||
builder: (_, cons) {
|
||||
final width = cons.maxWidth / 2 - 41;
|
||||
final width = cons.maxWidth / 2 - 6.5;
|
||||
return Column(
|
||||
children: [
|
||||
UIs.height13,
|
||||
@@ -264,10 +264,17 @@ class _ContainerPageState extends ConsumerState<ContainerPage> {
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 12, color: Colors.grey),
|
||||
UIs.width7,
|
||||
Text(value ?? l10n.unknown, style: UIs.text11Grey),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value ?? l10n.unknown,
|
||||
style: UIs.text11Grey,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -63,6 +63,9 @@ class _ServerDetailPageState extends ConsumerState<ServerDetailPage> with Single
|
||||
final _netSortType = ValueNotifier(_NetSortType.device);
|
||||
late final _collapse = _settings.collapseUIDefault.fetch();
|
||||
late final _textFactor = TextScaler.linear(_settings.textFactor.fetch());
|
||||
late final _cpuViewAsProgress = _settings.cpuViewAsProgress.fetch();
|
||||
late final _moveServerFuncs = _settings.moveServerFuncs.fetch();
|
||||
late final _displayCpuIndex = _settings.displayCpuIndex.fetch();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -97,7 +100,7 @@ class _ServerDetailPageState extends ConsumerState<ServerDetailPage> with Single
|
||||
}
|
||||
|
||||
Widget _buildMainPage(ServerState si) {
|
||||
final buildFuncs = !Stores.setting.moveServerFuncs.fetch();
|
||||
final buildFuncs = !_moveServerFuncs;
|
||||
final logo = _buildLogo(si);
|
||||
final children = <Widget>[if (logo != null) logo, if (buildFuncs) ServerFuncBtns(spi: si.spi)];
|
||||
for (final card in _cardsOrder) {
|
||||
@@ -197,7 +200,7 @@ class _ServerDetailPageState extends ConsumerState<ServerDetailPage> with Single
|
||||
]);
|
||||
}
|
||||
|
||||
final List<Widget> children = Stores.setting.cpuViewAsProgress.fetch()
|
||||
final List<Widget> children = _cpuViewAsProgress
|
||||
? _buildCPUProgress(ss.cpu)
|
||||
: [_buildCPUChart(ss)];
|
||||
|
||||
@@ -258,7 +261,7 @@ class _ServerDetailPageState extends ConsumerState<ServerDetailPage> with Single
|
||||
const kRowThreshold = 4;
|
||||
const kCoresCountThreshold = kMaxColumn * kRowThreshold;
|
||||
final children = <Widget>[];
|
||||
final displayCpuIndexSetting = Stores.setting.displayCpuIndex.fetch();
|
||||
final displayCpuIndexSetting = _displayCpuIndex;
|
||||
|
||||
if (cs.coresCount > kCoresCountThreshold) {
|
||||
final numCoresToDisplay = cs.coresCount - 1;
|
||||
|
||||
@@ -49,11 +49,12 @@ extension _Operation on _ServerPageState {
|
||||
await context.showRoundDialog(title: libL10n.attention, child: Text(l10n.suspendTip));
|
||||
Stores.setting.showSuspendTip.put(false);
|
||||
}
|
||||
srv.client?.execWithPwd(
|
||||
await srv.client?.execWithPwd(
|
||||
ShellFunc.suspend.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
|
||||
context: context,
|
||||
id: srv.id,
|
||||
);
|
||||
) ??
|
||||
(null, '');
|
||||
},
|
||||
typ: l10n.suspend,
|
||||
name: srv.spi.name,
|
||||
@@ -62,11 +63,13 @@ extension _Operation on _ServerPageState {
|
||||
|
||||
void _onTapShutdown(ServerState srv) {
|
||||
_askFor(
|
||||
func: () => srv.client?.execWithPwd(
|
||||
ShellFunc.shutdown.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
|
||||
context: context,
|
||||
id: srv.id,
|
||||
),
|
||||
func: () async {
|
||||
await srv.client?.execWithPwd(
|
||||
ShellFunc.shutdown.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
|
||||
context: context,
|
||||
id: srv.id,
|
||||
);
|
||||
},
|
||||
typ: l10n.shutdown,
|
||||
name: srv.spi.name,
|
||||
);
|
||||
@@ -74,11 +77,14 @@ extension _Operation on _ServerPageState {
|
||||
|
||||
void _onTapReboot(ServerState srv) {
|
||||
_askFor(
|
||||
func: () => srv.client?.execWithPwd(
|
||||
ShellFunc.reboot.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
|
||||
context: context,
|
||||
id: srv.id,
|
||||
),
|
||||
func: () async {
|
||||
await srv.client?.execWithPwd(
|
||||
ShellFunc.reboot.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
|
||||
context: context,
|
||||
id: srv.id,
|
||||
) ??
|
||||
(null, '');
|
||||
},
|
||||
typ: l10n.reboot,
|
||||
name: srv.spi.name,
|
||||
);
|
||||
|
||||
@@ -84,6 +84,7 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> {
|
||||
String? _streamingContent;
|
||||
String? _error;
|
||||
bool _isStreaming = false;
|
||||
bool _isMinimized = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -387,12 +388,23 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final bottomPadding = MediaQuery.viewInsetsOf(context).bottom;
|
||||
final heightFactor = _isMinimized ? 0.18 : 0.85;
|
||||
|
||||
return FractionallySizedBox(
|
||||
heightFactor: 0.85,
|
||||
return TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(end: heightFactor),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOutCubic,
|
||||
builder: (context, animatedHeightFactor, child) {
|
||||
return ClipRect(
|
||||
child: FractionallySizedBox(
|
||||
heightFactor: animatedHeightFactor,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||
child: Row(
|
||||
@@ -402,83 +414,96 @@ class _AskAiSheetState extends ConsumerState<_AskAiSheet> {
|
||||
if (_isStreaming)
|
||||
const SizedBox(height: 16, width: 16, child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: Icon(_isMinimized ? Icons.unfold_more : Icons.unfold_less),
|
||||
tooltip: libL10n.fold,
|
||||
onPressed: () {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
setState(() {
|
||||
_isMinimized = !_isMinimized;
|
||||
});
|
||||
},
|
||||
),
|
||||
IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop()),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView(
|
||||
if (!_isMinimized) ...[
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
children: [
|
||||
Text(context.l10n.askAiSelectedContent, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 6),
|
||||
CardX(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: SelectableText(
|
||||
widget.selection,
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(context.l10n.askAiConversation, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 6),
|
||||
..._buildConversationWidgets(context, theme),
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
children: [
|
||||
Text(context.l10n.askAiSelectedContent, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 6),
|
||||
CardX(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(_error!, style: TextStyle(color: theme.colorScheme.error)),
|
||||
child: SelectableText(
|
||||
widget.selection,
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(context.l10n.askAiConversation, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 6),
|
||||
..._buildConversationWidgets(context, theme),
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
CardX(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(_error!, style: TextStyle(color: theme.colorScheme.error)),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_isStreaming) ...[const SizedBox(height: 16), const LinearProgressIndicator()],
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
if (_isStreaming) ...[const SizedBox(height: 16), const LinearProgressIndicator()],
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
child: Text(
|
||||
context.l10n.askAiDisclaimer,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 8, 16, 16 + bottomPadding),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Input(
|
||||
controller: _inputController,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
hint: context.l10n.askAiFollowUpHint,
|
||||
action: TextInputAction.send,
|
||||
onSubmitted: (_) => _sendMessage(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Btn.icon(
|
||||
onTap: _isStreaming || _inputController.text.trim().isEmpty ? null : _sendMessage,
|
||||
icon: const Icon(Icons.send, size: 18),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
child: Text(
|
||||
context.l10n.askAiDisclaimer,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 8, 16, 16 + bottomPadding),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Input(
|
||||
controller: _inputController,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
hint: context.l10n.askAiFollowUpHint,
|
||||
action: TextInputAction.send,
|
||||
onSubmitted: (_) => _sendMessage(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Btn.icon(
|
||||
onTap: _isStreaming || _inputController.text.trim().isEmpty ? null : _sendMessage,
|
||||
icon: const Icon(Icons.send, size: 18),
|
||||
),
|
||||
],
|
||||
).cardx,
|
||||
),
|
||||
] else
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
).cardx,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ class SSHPageState extends ConsumerState<SSHPage>
|
||||
onTapUp: (_) => _virtKeyLongPressTimer?.cancel(),
|
||||
child: SizedBox(
|
||||
width: virtKeyWidth,
|
||||
height: _virtKeysHeight / _virtKeysList.length,
|
||||
height: _horizonVirtKeys ? _virtKeysHeight : _virtKeysHeight / _virtKeysList.length,
|
||||
child: Center(child: child),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -481,7 +481,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -608,7 +608,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -618,7 +618,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -638,7 +638,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1291;
|
||||
CURRENT_PROJECT_VERSION = 1297;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -649,7 +649,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1291;
|
||||
MARKETING_VERSION = 1.0.1297;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
24
pubspec.lock
24
pubspec.lock
@@ -205,10 +205,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_web
|
||||
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
|
||||
sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
version: "0.3.5+3"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -440,10 +440,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -727,18 +727,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive_ce
|
||||
sha256: "81d39a03c4c0ba5938260a8c3547d2e71af59defecea21793d57fc3551f0d230"
|
||||
sha256: "29f8791bf13fa6cf7435a58f1f82a7c9706973c867affa77c34d91e105762664"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.15.1"
|
||||
version: "2.17.0"
|
||||
hive_ce_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hive_ce_flutter
|
||||
sha256: "26d656c9e8974f0732f1d09020e2d7b08ba841b8961a02dbfb6caf01474b0e9a"
|
||||
sha256: "2677e95a333ff15af43ccd06af7eb7abbf1a4f154ea071997f3de4346cae913a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.3.4"
|
||||
hive_ce_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -831,10 +831,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: isolate_channel
|
||||
sha256: f3d36f783b301e6b312c3450eeb2656b0e7d1db81331af2a151d9083a3f6b18d
|
||||
sha256: "68191008e3a219bc87cc8cddbcd1e29810bd9f3a0fdc2108b574ccbd9aafda08"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+1"
|
||||
version: "0.3.0"
|
||||
isolate_contactor:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1750,10 +1750,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.4"
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: server_box
|
||||
description: server status & toolbox app.
|
||||
publish_to: "none"
|
||||
version: 1.0.1291+1291
|
||||
version: 1.0.1297+1297
|
||||
|
||||
environment:
|
||||
sdk: ">=3.9.0"
|
||||
|
||||
Reference in New Issue
Block a user