Skip to content

Conversation

@GT-610
Copy link
Contributor

@GT-610 GT-610 commented Jan 11, 2026

Part of #1000.

This PR focuses on container parsing. It will undergo continuous submission and improvement, and will be transitioned from draft to open once it is fully completed.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed layout and text overflow issues in container statistics display.
    • Improved server operation handling for suspend, shutdown, and reboot actions to ensure commands complete properly before proceeding.

✏️ Tip: You can customize this high-level summary in your review settings.

… content

Adjust the return type of the `execWithPwd` method to `(int?, String)` so that it can simultaneously return the exit code and output content

Fix the issue in ContainerNotifier where the return result of execWithPwd is not handled correctly

Ensure that server operations (shutdown/restart/suspend) are correctly pending until the command execution is completed
…ists

Support the simultaneous display of multiple container operation errors, enhancing error handling capabilities
…text overflow

Adjust the width calculation for the container page layout, changing from subtracting a fixed value to subtracting a smaller value to improve the layout

Add overflow ellipsis processing to the text to prevent anomalies when the text is too long
@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The SSH client's execWithPwd method return type was changed from Future<int?> to Future<(int?, String)> to return both exit code and command output as a tuple. This change propagates through the data provider layer with updated null-handling and mounted guards, UI callbacks are converted to async/await patterns, and layout adjustments ensure proper text overflow handling.

Changes

Cohort / File(s) Summary
Core SSH Client
lib/core/extension/ssh_client.dart
Updated execWithPwd method signature to return tuple (int?, String) instead of int?, now propagating both exit code and command output to callers.
Data Provider Updates
lib/data/provider/container.dart
Refactored to unpack tuple result from execWithPwd. Added null-conditional operator for client access and mounted-state guard to prevent stale updates.
UI Callbacks
lib/view/page/server/tab/utils.dart
Converted three action callbacks (_onTapSuspend, _onTapShutdown, _onTapReboot) from synchronous to async, now properly awaiting shell command execution.
UI Layout & Rendering
lib/view/page/container/container.dart
Adjusted container stats width calculation and added text overflow handling with Expanded widget to prevent layout issues in tight spaces.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A tuple hops where once an int did roam,
Exit codes now bring their output home,
Async calls await their shell's reply,
UI layouts bloom and never overflow—
Refactoring done with care and flow! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'opt: Better performance on container parsing' is vague and does not clearly reflect the actual changes, which involve modifying the execWithPwd API return type, updating callers, fixing UI layout issues, and ensuring async command completion. Use a more specific title that describes the primary changes, such as 'refactor: Update execWithPwd return type and callers' or break into multiple PRs with focused titles.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@GT-610
Copy link
Contributor Author

GT-610 commented Jan 11, 2026

@coderabbitai Review.

@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

@GT-610: I'll review the changes in this PR focused on container parsing optimization.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
lib/core/extension/ssh_client.dart (2)

115-151: Potential parsing regression: returned output may include stderr (e.g., sudo password prompt).
If exec() aggregates stderr into the returned string, consumers that parse stdout (like container output split/JSON) can break when sudo prompts/errors appear. Consider excluding stderr from the aggregated output for execWithPwd and relying on onStderr for stderr handling.

Proposed fix (stdout-only output)
   Future<(int?, String)> execWithPwd(
     String script, {
     String? entry,
     BuildContext? context,
     OnStdout? onStdout,
     OnStdout? onStderr,
     required String id,
   }) async {
     var isRequestingPwd = false;
     final (session, output) = await exec(
       (sess) {
         sess.stdin.add('$script\n'.uint8List);
         sess.stdin.close();
       },
       onStderr: (data, session) async {
         onStderr?.call(data, session);
         if (isRequestingPwd) return;

         if (data.contains('[sudo] password for ')) {
           isRequestingPwd = true;
           final user = Miscs.pwdRequestWithUserReg.firstMatch(data)?.group(1);
           final ctx = context ?? WidgetsBinding.instance.focusManager.primaryFocus?.context;
           if (ctx == null) return;
           final pwd = ctx.mounted ? await ctx.showPwdDialog(title: user, id: id) : null;
           if (pwd == null || pwd.isEmpty) {
             session.stdin.close();
           } else {
             session.stdin.add('$pwd\n'.uint8List);
           }
           isRequestingPwd = false;
         }
       },
       onStdout: onStdout,
       entry: entry,
+      stderr: false,
     );
     return (session.exitCode, output);
   }

115-151: Update all execWithPwd call sites to consistently handle the record return type.

The SDK supports Dart 3.9.0+ (which supports record types), but call sites are inconsistent. Three calls in lib/view/page/server/tab/utils.dart (lines 52, 66, 80) discard the return value entirely, while two calls in lib/data/provider/container.dart (lines 91, 236) properly destructure it as (code, raw) and (code, _) respectively. Either explicitly destructure the record at all call sites or assign to variables if the return values are unused, for consistency and clarity.

lib/data/provider/container.dart (1)

87-116: Handle client == null explicitly to avoid misleading segmentsNotMatch errors.
Lines 88-92: if client is null, raw stays empty and you’ll fall into the segments-length error path (Lines 106-115). Prefer early-return with a clearer error.

Proposed tweak
     final cmd = _wrap(ContainerCmdType.execAll(state.type, sudo: sudo, includeStats: includeStats));
     int? code;
     String raw = '';
     if (client != null) {
       (code, raw) = await client!.execWithPwd(cmd, context: context, id: hostId);
+    } else {
+      state = state.copyWith(
+        isBusy: false,
+        error: ContainerErr(type: ContainerErrType.unknown, message: 'SSH client not available'),
+      );
+      return;
     }

     if (!ref.mounted) return;
     state = state.copyWith(isBusy: false);
🧹 Nitpick comments (4)
lib/view/page/server/tab/utils.dart (2)

45-57: Avoid await on a nullable client; make the null-path explicit (and optionally handle the result).
Line 52: prefer final client = srv.client; if (client == null) return; await client.execWithPwd(...) for clearer typing/flow, and consider handling non-zero exit codes now that execWithPwd returns them.

Proposed tweak
       func: () async {
         if (Stores.setting.showSuspendTip.fetch()) {
           await context.showRoundDialog(title: libL10n.attention, child: Text(l10n.suspendTip));
           Stores.setting.showSuspendTip.put(false);
         }
-        await srv.client?.execWithPwd(
+        final client = srv.client;
+        if (client == null) return;
+        await client.execWithPwd(
           ShellFunc.suspend.exec(srv.spi.id, systemType: srv.status.system, customDir: null),
           context: context,
           id: srv.id,
         );
       },

63-75: Consider making _askFor.func awaitable so errors/ordering are observable.
Lines 65-71 and 79-85: these are now async, but _askFor currently invokes func() without awaiting (Line 132), so failures are effectively “fire-and-forget”. If the intent is “wait for completion” for these ops, making _askFor take Future<void> Function() and awaiting it in the OK handler will align behavior.

Proposed tweak (signature + call site)
-  void _askFor({required void Function() func, required String typ, required String name}) {
+  void _askFor({required Future<void> Function() func, required String typ, required String name}) {
     context.showRoundDialog(
       title: libL10n.attention,
       child: Text(libL10n.askContinue('$typ ${l10n.server}($name)')),
       actions: Btn.ok(
-        onTap: () {
+        onTap: () async {
           context.pop();
-          func();
+          await func();
         },
       ).toList,
     );
   }

Also applies to: 77-89

lib/view/page/container/container.dart (1)

233-258: Clamp computed width to avoid negative sizes on very small layouts.
Line 237: cons.maxWidth / 2 - 6.5 can go negative; prefer clamping to >= 0.

Proposed tweak
       builder: (_, cons) {
-        final width = cons.maxWidth / 2 - 6.5;
+        final width = math.max(0.0, cons.maxWidth / 2 - 6.5);
         return Column(
lib/data/provider/container.dart (1)

234-252: Tuple destructuring + fallback looks good; consider whether discarding output is intended.
Lines 236-245: (code, _) = ... ?? (null, null) is clean. Just double-check whether some failures only surface on stdout (you currently only return stderr-derived errs when code != 0).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06070c2 and cffa318.

📒 Files selected for processing (4)
  • lib/core/extension/ssh_client.dart
  • lib/data/provider/container.dart
  • lib/view/page/container/container.dart
  • lib/view/page/server/tab/utils.dart
🧰 Additional context used
📓 Path-based instructions (2)
lib/view/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/view/**/*.dart: Prefer widgets/utilities from fl_lib for common UI (e.g., CustomAppBar, context.showRoundDialog, Input, Btnx.cancelOk)
Prefer using libL10n strings before adding new ones to project l10n
Split UI into build, actions, and utils; use extension on to separate concerns

Files:

  • lib/view/page/container/container.dart
  • lib/view/page/server/tab/utils.dart
lib/data/provider/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Organize Riverpod providers by feature under lib/data/provider/

Files:

  • lib/data/provider/container.dart
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: lollipopkit/flutter_server_box PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T09:36:41.682Z
Learning: Applies to lib/data/model/container/**/*.dart : Place container/Docker models under lib/data/model/container/
📚 Learning: 2025-10-08T09:36:41.682Z
Learnt from: CR
Repo: lollipopkit/flutter_server_box PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T09:36:41.682Z
Learning: Applies to lib/data/model/container/**/*.dart : Place container/Docker models under lib/data/model/container/

Applied to files:

  • lib/view/page/container/container.dart
  • lib/data/provider/container.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check
🔇 Additional comments (1)
lib/view/page/container/container.dart (1)

261-283: Expanded + TextOverflow.ellipsis is a solid fix for overflow here.
Lines 271-277: good change to keep stats values single-line without layout exceptions on narrow widths.

@GT-610
Copy link
Contributor Author

GT-610 commented Jan 11, 2026

Noticed. I'll check these comments later.

Add detection for Podman Docker emulation in the container module. When detected, a prompt message will be displayed and users will be advised to switch to Podman settings.

Updated the multilingual translation files to support the new features.
@lollipopkit
Copy link
Owner

Still a draft?

@GT-610
Copy link
Contributor Author

GT-610 commented Jan 13, 2026

Still a draft?

Yeah. I haven't reviewed CodeRabbit issues yet.
If finished I'll convert draft to Open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants