Skip to content

enhance(ui): improve ux with a better UI#46

Merged
enegalan merged 3 commits into
mainfrom
28-enhanceui-improve-ux-with-a-better-ui
Jul 9, 2026
Merged

enhance(ui): improve ux with a better UI#46
enegalan merged 3 commits into
mainfrom
28-enhanceui-improve-ux-with-a-better-ui

Conversation

@enegalan

@enegalan enegalan commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added update checking with an update dialog, an “Updates” settings section, and an update badge.
    • Added a macOS-native menu bar with About/Preferences/Updates and navigation.
    • Added an optional “Open at login” setting.
    • Added an About dialog with app info and external links.
  • Bug Fixes
    • Improved sidebar collapsing/resizing behavior.
    • Enhanced logs wrapping/scrolling for smaller windows.
    • Set improved minimum window sizing on macOS and Windows.
  • Documentation / Tests
    • Updated changelog/roadmap and UI layout docs; added coverage for update and launch-at-login logic.

enegalan added 2 commits July 9, 2026 02:18
…cations

- Introduced a native macOS menu bar for improved navigation and access to settings.
- Implemented an optional feature to launch Calf automatically at user login across macOS, Linux, and Windows.
- Added update notifications that check for new releases and prompt users to download the latest version.
@enegalan enegalan linked an issue Jul 9, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 25a8a8db-7a92-4c8e-bade-669c0480da6f

📥 Commits

Reviewing files that changed from the base of the PR and between d3878ba and a05f44d.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • ui/lib/app_shell.dart
  • ui/lib/platform/launch_at_login.dart
  • ui/lib/platform/macos_menu.dart
  • ui/lib/platform/open_url.dart
  • ui/lib/storage/sidebar_preferences.dart
  • ui/lib/storage/update_preferences.dart
  • ui/lib/updates/update_checker.dart
  • ui/lib/widgets/about_dialog.dart
  • ui/test/update_checker_test.dart
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • ui/lib/storage/sidebar_preferences.dart
  • ui/test/update_checker_test.dart
  • ui/lib/widgets/about_dialog.dart
  • ui/lib/storage/update_preferences.dart
  • ui/lib/updates/update_checker.dart
  • ui/lib/platform/launch_at_login.dart
  • ui/lib/app_shell.dart

📝 Walkthrough

Walkthrough

Adds update-check flow, macOS native menus, launch-at-login support, a collapsible sidebar with persisted state, update and about dialogs, UI indicators, native runner sizing changes, and matching documentation updates.

Changes

Update notifications, macOS menu, launch-at-login, and UI integration

Layer / File(s) Summary
Update data model
ui/lib/updates/update_info.dart
Defines UpdateInfo and UpdateCheckResult used by the update flow.
Update check flow and storage
ui/lib/storage/update_preferences.dart, ui/lib/updates/update_checker.dart, ui/test/update_checker_test.dart
Implements cached GitHub release checking, update preference persistence, version comparison/parsing, and supporting tests.
macOS menu and dialog links
ui/lib/platform/open_url.dart, ui/lib/platform/macos_menu.dart, ui/lib/widgets/about_dialog.dart
Adds GitHub URL getters, the native macOS menu bar wrapper, and the about dialog with external links.
Launch-at-login support
ui/lib/platform/launch_at_login.dart, ui/test/launch_at_login_test.dart
Adds cross-platform launch-at-login path resolution and enable/disable handling with path-resolution tests.
AppShell shell wiring
ui/lib/app_shell.dart
Wires update checking, sidebar collapse state, navigation helpers, and native menu integration into the shell layout.
SettingsScreen update and login UI
ui/lib/app_shell.dart
Extends settings state and UI for update checks, launch-at-login, skipped-version handling, and version/download actions.
Update badge, logs, and native window sizing
ui/lib/widgets/app_top_bar.dart, ui/lib/widgets/logs_panel.dart, ui/linux/runner/*, ui/macos/Runner/MainFlutterWindow.swift, ui/windows/runner/win32_window.cpp
Adds the update badge, width-aware log rendering, Linux application rename, and minimum window sizing on desktop runners.
Documentation and roadmap updates
.cursor/rules/calf.mdc, CLAUDE.md, CHANGELOG.md, ROADMAP.md
Updates module references, changelog entries, and roadmap status text.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AppShell
  participant UpdateChecker
  participant UpdatePreferences
  participant GitHubAPI
  participant UpdateDialog

  AppShell->>UpdateChecker: checkForUpdates(currentVersion)
  UpdateChecker->>UpdatePreferences: load()
  UpdatePreferences-->>UpdateChecker: cached preferences
  alt cache stale or forced
    UpdateChecker->>GitHubAPI: fetch latest release
    GitHubAPI-->>UpdateChecker: release JSON
    UpdateChecker->>UpdatePreferences: saveCheckResult(...)
  end
  UpdateChecker-->>AppShell: UpdateCheckResult
  alt update available
    AppShell->>UpdateDialog: showUpdateAvailableDialog(...)
    UpdateDialog-->>AppShell: onSkip / onDownload
    AppShell->>UpdatePreferences: saveSkippedVersion(version)
  end
Loading
sequenceDiagram
  participant User
  participant MacosMenuScope
  participant AppShell
  participant SettingsScreen
  participant LaunchAtLogin

  User->>MacosMenuScope: select Preferences or Update action
  MacosMenuScope->>AppShell: onOpenSettings / onCheckForUpdates
  AppShell->>SettingsScreen: build(appVersion, update state)
  User->>SettingsScreen: toggle Open at login
  SettingsScreen->>LaunchAtLogin: setEnabled(true/false)
  LaunchAtLogin-->>SettingsScreen: success or failure
Loading

Possibly related PRs

  • enegalan/calf#9: Both PRs modify ui/lib/app_shell.dart and the surrounding shell/settings flow.
  • enegalan/calf#23: Both PRs modify ui/lib/widgets/logs_panel.dart, including _LogTextView rendering behavior.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and does not clearly describe the main changes, which span updates, menus, storage, and platform-specific UI work. Use a specific title such as 'Add update checking, macOS menu integration, and launch-at-login support'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 28-enhanceui-improve-ux-with-a-better-ui

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (6)
ui/lib/widgets/about_dialog.dart (2)

68-93: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Read theme from context inside _AboutLink instead of passing it.

_AboutLink receives ShadThemeData as a constructor parameter, but its build method already has a BuildContext. Calling ShadTheme.of(context) directly is more idiomatic and removes the coupling. This also eliminates the need to pass theme at both call sites (lines 44 and 50).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/widgets/about_dialog.dart` around lines 68 - 93, _AboutLink is taking
ShadThemeData through its constructor even though build already has
BuildContext; update _AboutLink to read the theme with ShadTheme.of(context)
inside build instead of storing/passing theme. Remove the theme parameter from
_AboutLink and adjust both call sites in about_dialog.dart to stop supplying it,
keeping the label and onPressed wiring unchanged.

4-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid importing macos_menu.dart for URL constants.

A generic widget file depends on a platform-specific menu module solely for calfRepositoryUrl and calfDocumentationUrl. This creates an inverted dependency direction — widgets should not depend on platform-specific code. Extract these URL constants into a shared location (e.g., a constants file or the open_url.dart module that already deals with external links).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/widgets/about_dialog.dart` around lines 4 - 5, The about dialog widget
is depending on a platform-specific menu module only to access URL constants,
which creates an unwanted inverted dependency. Move `calfRepositoryUrl` and
`calfDocumentationUrl` into a shared, non-platform-specific location such as
`open_url.dart` or a dedicated constants file, then update `AboutDialog` to
import that shared source instead of `macos_menu.dart`.
ui/lib/updates/update_checker.dart (1)

179-197: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Hoist the RegExp to a static field.

RegExp(r'^\d+') is compiled on every _parseVersionParts call. Making it a static final avoids repeated compilation on the hot version-comparison path.

♻️ Proposed refactor
 class UpdateChecker {
+  static final _leadingDigits = RegExp(r'^\d+');
+
   UpdateChecker({http.Client? client}) : _client = client ?? http.Client();
-      final match = RegExp(r'^\d+').firstMatch(parts[index]);
+      final match = _leadingDigits.firstMatch(parts[index]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/updates/update_checker.dart` around lines 179 - 197, Hoist the
version-matching RegExp out of _parseVersionParts in UpdateChecker so it is
reused instead of recompiled on every call. Add a static final RegExp field on
the same class and update _parseVersionParts to use that field when extracting
numeric prefixes from the normalized version parts.
ui/lib/app_shell.dart (1)

228-237: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Defer the saveCollapsed side effect out of build().

Modifying _lastWidthWasSmall and _isCollapsed directly in build() works because the values are consumed in the same build cycle, but calling SidebarPreferences.saveCollapsed() is a side effect that should not run during build(). Defer it with addPostFrameCallback to keep build() pure.

♻️ Proposed refactor
     } else if (_lastWidthWasSmall != isSmallScreen) {
       _lastWidthWasSmall = isSmallScreen;
       _isCollapsed = isSmallScreen;
-      SidebarPreferences.saveCollapsed(_isCollapsed);
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        SidebarPreferences.saveCollapsed(_isCollapsed);
+      });
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/app_shell.dart` around lines 228 - 237, Move the
SidebarPreferences.saveCollapsed side effect out of AppShell’s build path so
build() stays pure. In the AppShell logic where _lastWidthWasSmall and
_isCollapsed are updated, keep only the state updates during build and defer the
saveCollapsed call via addPostFrameCallback. Use the existing AppShell state
fields and SidebarPreferences.saveCollapsed as the main points to adjust,
preserving the current collapse behavior while avoiding side effects during
rendering.
ui/test/update_checker_test.dart (1)

1-40: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add tests for check() and cache behavior.

The test suite covers static helpers (compareVersions, normalizeTagName, parseReleaseJson, preferredAssetNames) but not the main check() method, _selectDownloadUrl, cache expiry logic, or error-handling paths. Injecting a mock http.Client (already supported via the constructor) would allow testing network success/failure/cache scenarios without real I/O.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/test/update_checker_test.dart` around lines 1 - 40, Add coverage for the
main UpdateChecker behavior, not just the static helpers. Extend the tests to
instantiate UpdateChecker with a mock http.Client and verify check() success,
failure, and cached-result paths, including cache expiry behavior. Also add
assertions around _selectDownloadUrl to ensure the preferred asset is chosen
correctly, and exercise error-handling so network or parsing failures are
covered without real I/O.
ui/lib/platform/launch_at_login.dart (1)

67-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace generic catch (_) with specific error handling or logging.

All nine catch blocks silently swallow every exception, making it impossible to diagnose why a toggle failed. This violates the project's established practice of handling each error case individually so failures are identifiable.

At minimum, log the error before returning false:

♻️ Example for `_macEnable` (apply similarly elsewhere)
   try {
       plist.parent.createSync(recursive: true);
       await plist.writeAsString(_macLaunchAgentPlist(launchPath));
       return true;
-    } catch (_) {
+    } catch (e) {
+      // log or surface the specific failure
       return false;
     }

Based on learnings: "Do not use generic catch-all error handling; handle each specific error case individually so logs, UI, and API responses clearly identify what failed and why."

Also applies to: 93-95, 107-109, 147-149, 166-168, 180-182, 204-206, 218-220, 228-229

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/platform/launch_at_login.dart` around lines 67 - 80, The generic catch
blocks in launch_at_login.dart are swallowing all exceptions without any
diagnostics. Update each affected try/catch in methods like _macIsEnabled (and
the other listed helpers) to catch specific errors where possible, or at minimum
log the caught error before returning false. Keep the handling consistent with
the project’s pattern so failures in launch-at-login toggles can be identified
from logs.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 8-24: The changelog has three repeated Unreleased headings and the
update note is written from the implementation perspective. In CHANGELOG.md,
merge the entries under a single `## [Unreleased]` section, keep the three added
items together, and rewrite the update notifications bullet in user-facing
language without mentioning GitHub Releases or other implementation details.

In `@ui/lib/app_shell.dart`:
- Around line 78-85: The loadAppVersion method is swallowing failures from
fetchStatus(), which leaves _appVersion unset and prevents checkForUpdates from
running. Update the catch block in loadAppVersion to handle the error
explicitly: log or surface the failure with enough context, and make sure the UI
state is updated so the settings screen does not stay on “Loading version...”.
Use the loadAppVersion, fetchStatus, and checkForUpdates symbols to locate and
adjust this flow.

In `@ui/lib/platform/launch_at_login.dart`:
- Around line 209-221: The Windows launch-at-login registry entry in
_windowsEnable stores launchPath unquoted, so update the reg add call to write a
properly quoted command value for the Run key. Use the _windowsEnable and
launchAtLoginPath logic to ensure the path is wrapped/escaped for Windows
startup parsing, and keep the stored value consistent with what
_windowsIsEnabled checks so paths with spaces are handled correctly.

In `@ui/lib/platform/macos_menu.dart`:
- Line 85: The `macos_menu.dart` menu builders are using `if (... case final
item?) item,` patterns that violate the `use_null_aware_elements` lint and block
CI. Update each affected element in the menu list constructors to use the
null-aware element form instead, so the item is included only when
`_platformMenu(...)` returns non-null. Focus on the repeated menu-building logic
around `_platformMenu` and `PlatformProvidedMenuItemType` occurrences, and
replace all nine matching cases consistently.

In `@ui/lib/storage/sidebar_preferences.dart`:
- Around line 7-23: The sidebar preference persistence code uses generic
catch-all handling in loadCollapsed and saveCollapsed, which hides failures and
makes diagnosis impossible. Update SidebarPreferences to handle expected
failures explicitly: in loadCollapsed, catch FileSystemException and
FormatException separately around CalfUiStorage.file, readAsStringSync, and
jsonDecode, returning defaultCollapsed only for those known cases. In
saveCollapsed, replace the empty catch with at least logging the write failure
(and preserving the exception details) so callers can tell when sidebar.json
could not be written.

In `@ui/lib/storage/update_preferences.dart`:
- Around line 49-51: The `load()` and `_save()` error handlers in
`UpdatePreferencesStore` are swallowing exceptions, which hides failures and can
silently drop persisted update state. Replace the blanket `catch (_)` blocks
with specific exception handling where possible, and make sure the caught error
is logged with enough context to diagnose the failure. For `_save()`, avoid
suppressing the failure entirely and let the error propagate or return a failure
signal so callers can react. Keep the fix scoped around the `load()` and
`_save()` methods and their existing `catch` blocks.

In `@ui/lib/updates/update_checker.dart`:
- Around line 111-117: Add a timeout to the GitHub request in
UpdateChecker.check() so a stalled call to _client.get(...) cannot leave the UI
stuck on “Checking...”. Update the logic around the existing _client.get call to
apply a deadline with .timeout(...), and handle TimeoutException in the same
failure path used for other network errors so the update state is reset and
retries remain possible without restarting the app.

In `@ui/lib/widgets/about_dialog.dart`:
- Line 46: The About dialog button handlers currently fire-and-forget the result
of openExternalUrl, so failures are silently ignored. Update the onPressed
callbacks in the About dialog widget to await openExternalUrl(calfRepositoryUrl)
and the other external link call, then handle a false result by surfacing user
feedback such as a snackbar or message. Use the existing About dialog callback
locations and openExternalUrl return value to ensure failures are not swallowed.

In `@ui/test/update_checker_test.dart`:
- Around line 36-39: The UpdateChecker.preferredAssetNames test is
platform-dependent because it asserts a macOS-only asset name unconditionally.
Update the test in update_checker_test.dart to either skip or branch on
Platform.isMacOS before expecting Calf-1.2.3.dmg, or expand the assertions so
the expected asset list matches the current platform rather than always assuming
macOS.

---

Nitpick comments:
In `@ui/lib/app_shell.dart`:
- Around line 228-237: Move the SidebarPreferences.saveCollapsed side effect out
of AppShell’s build path so build() stays pure. In the AppShell logic where
_lastWidthWasSmall and _isCollapsed are updated, keep only the state updates
during build and defer the saveCollapsed call via addPostFrameCallback. Use the
existing AppShell state fields and SidebarPreferences.saveCollapsed as the main
points to adjust, preserving the current collapse behavior while avoiding side
effects during rendering.

In `@ui/lib/platform/launch_at_login.dart`:
- Around line 67-80: The generic catch blocks in launch_at_login.dart are
swallowing all exceptions without any diagnostics. Update each affected
try/catch in methods like _macIsEnabled (and the other listed helpers) to catch
specific errors where possible, or at minimum log the caught error before
returning false. Keep the handling consistent with the project’s pattern so
failures in launch-at-login toggles can be identified from logs.

In `@ui/lib/updates/update_checker.dart`:
- Around line 179-197: Hoist the version-matching RegExp out of
_parseVersionParts in UpdateChecker so it is reused instead of recompiled on
every call. Add a static final RegExp field on the same class and update
_parseVersionParts to use that field when extracting numeric prefixes from the
normalized version parts.

In `@ui/lib/widgets/about_dialog.dart`:
- Around line 68-93: _AboutLink is taking ShadThemeData through its constructor
even though build already has BuildContext; update _AboutLink to read the theme
with ShadTheme.of(context) inside build instead of storing/passing theme. Remove
the theme parameter from _AboutLink and adjust both call sites in
about_dialog.dart to stop supplying it, keeping the label and onPressed wiring
unchanged.
- Around line 4-5: The about dialog widget is depending on a platform-specific
menu module only to access URL constants, which creates an unwanted inverted
dependency. Move `calfRepositoryUrl` and `calfDocumentationUrl` into a shared,
non-platform-specific location such as `open_url.dart` or a dedicated constants
file, then update `AboutDialog` to import that shared source instead of
`macos_menu.dart`.

In `@ui/test/update_checker_test.dart`:
- Around line 1-40: Add coverage for the main UpdateChecker behavior, not just
the static helpers. Extend the tests to instantiate UpdateChecker with a mock
http.Client and verify check() success, failure, and cached-result paths,
including cache expiry behavior. Also add assertions around _selectDownloadUrl
to ensure the preferred asset is chosen correctly, and exercise error-handling
so network or parsing failures are covered without real I/O.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 751b27ea-a682-47fa-8ce8-ba96bfc1dfb8

📥 Commits

Reviewing files that changed from the base of the PR and between a5ba61b and d3878ba.

📒 Files selected for processing (24)
  • .cursor/rules/calf.mdc
  • CHANGELOG.md
  • CLAUDE.md
  • ROADMAP.md
  • ui/lib/app_shell.dart
  • ui/lib/platform/launch_at_login.dart
  • ui/lib/platform/macos_menu.dart
  • ui/lib/storage/sidebar_preferences.dart
  • ui/lib/storage/update_preferences.dart
  • ui/lib/updates/update_checker.dart
  • ui/lib/updates/update_dialog.dart
  • ui/lib/updates/update_info.dart
  • ui/lib/widgets/about_dialog.dart
  • ui/lib/widgets/app_top_bar.dart
  • ui/lib/widgets/logs_panel.dart
  • ui/linux/runner/CMakeLists.txt
  • ui/linux/runner/calf_application.cc
  • ui/linux/runner/calf_application.h
  • ui/linux/runner/main.cc
  • ui/linux/runner/my_application.h
  • ui/macos/Runner/MainFlutterWindow.swift
  • ui/test/launch_at_login_test.dart
  • ui/test/update_checker_test.dart
  • ui/windows/runner/win32_window.cpp
💤 Files with no reviewable changes (1)
  • ui/linux/runner/my_application.h

Comment thread CHANGELOG.md Outdated
Comment thread ui/lib/app_shell.dart
Comment thread ui/lib/platform/launch_at_login.dart
Comment thread ui/lib/platform/macos_menu.dart Outdated
Comment thread ui/lib/storage/sidebar_preferences.dart
Comment thread ui/lib/storage/update_preferences.dart Outdated
Comment thread ui/lib/updates/update_checker.dart Outdated
Comment thread ui/lib/widgets/about_dialog.dart Outdated
Comment thread ui/test/update_checker_test.dart Outdated
- Updated CHANGELOG to reflect the addition of update notifications and other features in version 0.9.0.
- Enhanced error handling in app version loading, sidebar preferences, and update preferences to provide more informative debug messages.
- Refactored macOS menu and launch at login functionalities for better code clarity and maintainability.
@enegalan enegalan merged commit 7e6f748 into main Jul 9, 2026
3 of 5 checks passed
@enegalan enegalan deleted the 28-enhanceui-improve-ux-with-a-better-ui branch July 9, 2026 15:09
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.

enhance(ui): Improve UX with a better UI

1 participant