Skip to content

Resolve small layout divergences with targeted moves - #817

Draft
alvst wants to merge 6 commits into
thaw-app:integration/menubar-scramblefrom
alvst:codex/fix-targeted-layout-divergence
Draft

Resolve small layout divergences with targeted moves#817
alvst wants to merge 6 commits into
thaw-app:integration/menubar-scramblefrom
alvst:codex/fix-targeted-layout-divergence

Conversation

@alvst

@alvst alvst commented Jul 26, 2026

Copy link
Copy Markdown

Scope

This is one isolated, release-appropriate reliability fix from a broader stabilization effort. It is not the deferred full feature implementation.

The broader 2.1 feature work remains parked and will be re-submitted against the appropriate 2.1 integration branch when that branch is available. This PR is intentionally limited to a small fix suitable for integration/sanitize-dev.

Change

Detects up to two divergent menu-bar items and moves them directly to their saved section boundary. Larger or unsafe divergences continue to use the existing full-profile fallback.

Why

Avoids an unnecessary full layout reapply when only a small number of items have drifted.

Validation

  • Built and tested from the clean integration/sanitize-dev base.
  • Included in a 72-hour normal-use soak with the other isolated reliability fixes.

diazdesandi and others added 6 commits July 5, 2026 11:48
Tagline now says "macOS 26 and macOS 27", the roadmap no longer lists
macOS 27 support as a future item, and the features list gains a
"macOS 27 (Golden Gate) support" entry. The requirements badge stays
at "macOS 26+" (still the correct minimum) and the issue thaw-app#687 status
banner is left in place.
…-app#761)

* fix(.gitignore): reorganize entries and restore ignored patterns for Xcode build artifacts

Updates the project ignore file to better organize patterns and ensures that essential Xcode build artifacts and internal configuration files remain properly excluded from version control.

* fix(project): skip App Store deployment for asset catalog compilation

* fix(build): update Xcode version to 26.6 for DMG build process

* build: gate SwiftLint script phase on declared inputs instead of running every build

alwaysOutOfDate = 1 forced the SwiftLint phase to re-run on every
single build, including no-op incremental rebuilds where no linted
file changed. Cost is negligible at the project's current size
(~0.16s per xcodebuild-showBuildSettings-measured run) but scales with
the codebase, and it's free to fix now.

Declare the linted sources (matching .swiftlint.yml's included paths:
MenuBarItemService, Shared, Thaw) via scripts/swiftlint-inputs.xcfilelist
and a stamp file as the phase's output, so Xcode's dependency analysis
can skip the phase when none of those files changed. Verified: a
touched linted file re-triggers the phase; an unrelated rebuild skips
it.

Note: the file list is a static snapshot and needs regenerating when
Swift files are added or removed under those three directories,
otherwise new files won't trigger a re-lint until the list catches up.

* build: enable eager linking and compilation caching

EAGER_LINKING was unset (defaults to NO) in Debug across all three
targets, so the linker waited for compilation to fully finish instead
of starting on already-compiled objects. Setting it to YES at the
project level lets the linker overlap with compilation, reducing Debug
build+link wall time.

COMPILATION_CACHE_ENABLE_CACHING was also unset (defaults off).
Enabling it caches Swift/Clang compilation results so repeated
compilations of unchanged inputs are served from cache -- the biggest
win is branch switching and clean builds, both common on this
long-lived feature branch. Local benefit only unless CI also caches
the CAS output path.

* build: add .build-benchmark to .gitignore

* build(ci): add caching for Swift compilation to improve build performance

* fix(build): address PR review for gitignore, SwiftLint inputs, and CI drift

Move .github/actions/build negations below build/ so new action files stay
trackable, regenerate swiftlint-inputs.xcfilelist from the current tree,
and add a generator script plus CI diff check to catch future drift.

* fix(ci): use bash for SwiftLint input generator on Ubuntu

Ubuntu's /bin/sh (dash) rejects set -o pipefail, which broke the static
analysis drift check on pull_request CI.
…haw-app#763)

* feat(settings): add searchable settings index

Catalog every user-facing settings control with pane, section, keywords,
and optional property links so sidebar search can fuzzy-match settings
without introducing duplicate translation keys.

* test(settings): add settings search index coverage

Verify pane coverage, relevance sorting, and drift-guard parity between
indexed properties and published settings models.

* feat(settings): add fuzzy search model for sidebar

Fuse-match the static settings index and group ranked results by pane for
the sidebar results list.

* feat(settings): add custom search field and results list

Replace the native searchable modifier with a pinned search field and a
polished grouped results list with hover and pressed feedback.

* feat(settings): integrate sidebar search in SettingsView

Swap the sidebar between pane navigation and search results, navigating
to the selected pane and clearing the query on selection.

* chore(l10n): add settings search UI strings

Add catalog entries for the sidebar search prompt and empty-results message.

* refactor(search): share SettingsSearchIndex ranking across menu-bar-item search

Extracts the relevance-sort/weighting recipe into a shared SearchRanker so
settings search and menu bar item search can't silently drift apart; behavior
is unchanged for both callers.

* style(search): adopt glass effect for settings and menu bar search fields

* refactor(settings): move settings search into Search/ folder, drop Settings prefix

Move SearchRanker, SettingsSearchIndex/Model/Views into
Thaw/Settings/Search/ and rename the SettingsSearch* types to Search*
now that the folder makes the "settings" context redundant.
SettingsProperty is left as-is since it names a settings-model
property, not a search concept.

* fix(settings): gate sidebar search and index behind macOS 27

Settings search UI and experimental index rows only activate on macOS 27,
keeping macOS 26 builds and drift-guard tests compatible with development.

* fix(settings): decouple search PR from onboarding and macOS 27 settings

Drop the replay-onboarding About pane callback until the onboarding PR
lands, dedupe macOS 27-only index rows from shared advanced entries, and
relax the property drift guard for experimental keys not yet on integration.

* fix(settings): address search PR review feedback

Add advanced-property drift assertions, auto-refresh SearchModel on
searchText changes, consolidate search result row shape, and drop the
tempShowInterval index row until that setting lands on integration.

* chore(build): sync SwiftLint inputs with Settings Search sources

Adds the four Thaw/Settings/Search/*.swift files after merging
integration/sanitize-dev CI drift checking (thaw-app#761).

* fix(settings): enable sidebar search on macOS 26

Show SettingsSearchSidebar on all supported releases; macOS 27-only index
rows remain gated in SearchIndex.entries at runtime.
…ntal) (thaw-app#764)

* refactor(onboarding): run feature tour before permissions in one window

Sequence onboarding and permission requests in PermissionsWindow, simplify
OnboardingSheet to a dismissible tour, and harden screen capture checks.

* feat(onboarding): replace skip button action with finish onboarding

* feat(onboarding): replace legacy onboarding with glass tour-based flow

Removes the old slide/sheet-based onboarding entirely and replaces it
with a redesigned, Liquid Glass tour: an animated welcome scene, four
looping feature slides (management, appearance, hotkeys, profiles),
and a permissions screen that reads real Accessibility/Screen
Recording status live. Reused by both the first-launch permissions
window and the Settings replay sheet.

Also adds AppPermissions.refreshPermissionsState() / Permission.refreshStatus()
so the tour can force an immediate re-check right after the user grants
access in System Settings, instead of waiting on the polling timer.

* fix(onboarding): stop tour from showing twice after a defaults reset

After `defaults delete`, AppKit's window restoration can bring the
Settings window back at launch; its visibility observer independently
called presentOnboardingIfNeeded(), racing with the dedicated
.permissions window that was already showing the same tour for the
same first launch.

presentOnboardingIfNeeded() now requires hasCompletedFirstLaunch, so it
only ever self-heals after first-launch setup is done — the
.permissions window is the sole owner of onboarding during an actual
first launch.

Separately, if required permissions are already granted despite
looking like a first launch (defaults wiped, but the system's TCC
grants weren't), mark onboarding as seen so only the permissions
screen resurfaces instead of replaying the full tour.

* feat(settings): add Replay Onboarding button to About pane

Lets a user re-trigger the onboarding tour on demand from the About
pane, reusing the existing isOnboardingPresented sheet already wired
up in SettingsWindow — unlike presentOnboardingIfNeeded(), this always
presents regardless of whether onboarding was already seen.

* fix(permissions): register Thaw for Screen Recording via CGRequestScreenCaptureAccess

SCShareableContent.getWithCompletionHandler alone was leaving Thaw entirely absent from System Settings' Screen Recording list on macOS 27, even after a full relaunch. Also call CGRequestScreenCaptureAccess() (the documented public API for adding an app to that list), activate the app first since Thaw is an LSUIElement agent with no Dock icon, and log the outcome of both calls instead of discarding errors silently.

* fix(permissions): show onboarding permission flow

Replace the old standalone permissions window content with the onboarding permissions screen for both first launch and missing-permission startup recovery.

Back the onboarding permissions UI with the real AppPermissions objects so grant buttons use Permission.performRequest(), including the Screen Recording TCC request path, instead of a separate demo permission model.

Remove the obsolete PermissionsView implementation and its now-unused PermissionsManaging abstraction. Keep the onboarding tour feature-only while routing the post-tour and permission-recovery stages through the shared permissions screen.

Also harden Screen Recording permission requests for LSUIElement mode by temporarily switching to regular activation, clearing stale cached permission state before requesting, and updating the cache when the public request or ScreenCaptureKit fallback succeeds.

* fix(onboarding): restore permissions screen copy

Keep the shared onboarding permissions screen backed by real AppPermissions actions, but render the shorter onboarding-specific permission descriptions so startup recovery and first-launch onboarding match visually.

Restore the concise required-permission footnote to avoid the clipped taller layout seen with the generic permission details.

* fix(onboarding): update accessibility message and adjust layout for permission button

* style(onboarding): fix GPL file headers for SwiftLint strict mode

* fix(onboarding): address PR review for permissions, localization, and tests

Source onboarding permission copy from Permission.shortDetails, remove duplicate
screen-recording polling, localize tour slides, and add ThawWelcomeModel coverage.

* fix(onboarding): refresh screen recording status and localize privacy copy

Force fresh screen capture checks on poll and when returning from System
Settings, and route privacy panel strings through LocalizedStringKey.

* chore(ci): regenerate SwiftLint inputs for onboarding refactor

Update swiftlint-inputs.xcfilelist after replacing legacy onboarding files
with the glass tour and permissions flow sources.

* fix(onboarding): resolve NSApp isolation and Continue button reactivity

Mark ScreenCapture.requestPermissions @mainactor, observe AppPermissions
directly in the permissions step, and refresh grants when the app becomes
active so Continue enables after System Settings grants.
…#765)

* feat(layout): add reset target picker with legacy section moves

Extract macOS 26 layout reset paths into LayoutResetLegacy.swift and
replace the single Hidden-only button with a Reset to Visible/Hidden/
Always Hidden confirmation dialog in Menu Bar Layout settings.

* chore(build): add LayoutResetLegacy.swift to SwiftLint input list

* fix(layout): correct always-hidden reset band and review nits

Use MenuBarItemManager's always-hidden boundary in LayoutResetLegacy,
extend LayoutResetError test coverage, and align reset UI copy and success
messages.

* fix(layout): move always-hidden reset items left of AH control

rightOfItem(alwaysHidden) inserts into the Hidden section per ItemCache;
leftOfItem is the boundary used everywhere else for Always Hidden.

* chore(layout): remove unused controlItems reset parameter

Drop the unused ControlItemPair argument from
layoutResetMoveItemsToAlwaysHidden after the band fix no longer needed it.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d25cf2e4-c908-4260-abdd-cdf3716ad624

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added the menubar Hide/show, sections, control items, backends, capacity label Jul 26, 2026
@alvst
alvst marked this pull request as ready for review July 26, 2026 21:27
@diazdesandi
diazdesandi changed the base branch from integration/sanitize-dev to integration/menubar-scramble July 29, 2026 13:56
@diazdesandi
diazdesandi self-requested a review as a code owner July 29, 2026 13:56
@diazdesandi

Copy link
Copy Markdown
Member

Two blockers:

  1. Branch noise. This forked with chore(ci): land build and CI hygiene from macOS 27 experimental #761feat(layout): reset target picker with legacy section moves #765 still on it, so GitHub shows ~50 files. The real change is just 3188fca0 (one file in MenuBarItemManager). Please rebase onto latest integration/menubar-scramble and drop the unrelated commits.

  2. Design. applyTargetedSectionMoves parks items at the section edge, not their saved index. The divergence check only looks at section membership, so wrong order inside a section never gets fixed afterward.

#822 attacks the same “transient widget → full notched re-drag” symptom by trimming the full-sort replay. I’d like to land that first and see if this is still needed. If it is, please redo it so moves go to the saved index, with planning as a pure LayoutSolver helper that’s easy to test.

@diazdesandi
diazdesandi marked this pull request as draft July 29, 2026 14:27
@diazdesandi diazdesandi added the parked Waiting for new version label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

menubar Hide/show, sections, control items, backends, capacity parked Waiting for new version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants