Let users toggle popup-card suggestion display from Settings and the menu#351
Merged
Conversation
…menu Phase 1 only triggered the new render mode when caret geometry was estimated. This adds a persistent global preference (Auto / Inline / Popup) so users can pin one style for every app, and surfaces it both in the Settings General section and in the menu bar pop-up so it's reachable without opening Settings. The setting flows through `SuggestionSettingsSnapshot.mirrorPreference` and the overlay controller reads it live each presentation, so the toggle takes effect on the very next completion without a restart. Defaults remain `.auto`, so existing users see identical behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #347. That PR triggered the popup-card render mode automatically when caret geometry came back as
.estimated. This PR adds a persistent global preference (Auto / Inline / Popup) so users can pin one style for every app, and surfaces the picker both in the Settings General section and the menu bar pop-up so it's reachable without opening Settings.Validation
Two new tests cover persistence (
test_mirrorPreference_defaultsToAutoAndPersists) and the snapshot publisher (test_snapshotPublisher_emitsWhenMirrorPreferenceChanges). All existing snapshot/fixture call sites pick up the new field through a defaulted argument.Local
xcodebuild testexecution hits the known Team ID code-signing mismatch on this machine (called out in CLAUDE.md). Tests compile correctly and need CI signing or Xcode UI to execute.Risk / rollout notes
.auto, so existing users see identical behavior to Add mirror overlay fallback for unreliable caret geometry #347. Popup card still only appears for.estimatedcaret geometry unless the user pins.alwaysMirror.mirrorPreferencefield onSuggestionSettingsSnapshot. Construction is centralized in two places (the model'ssnapshotgetter andsnapshotPublisherchain) and the shared test fixture; all sites updated.snapshotPublisher'scontextTogglesgroup went fromCombineLatesttoCombineLatest3so the inner combine stack stays under Combine's 4-upstream cap; renamed topresentationTogglesto reflect the wider scope.OverlayControllernow reads the preference live pershowSuggestioncall via a computedcurrentRenderModePolicy. Tests can still inject a fixed policy via the renamedrenderModePolicyOverride:init parameter.setCurrentBundleIdentifierplumbing stays dormant for a follow-up phase.Linked issues
Refs #347.
Greptile Summary
Adds a persistent
MirrorPreferencesetting (Auto / Inline / Popup) that lets users pin a suggestion display style globally, surfaced in both Settings and the menu bar. The default is.auto, preserving existing behavior for all current users.SuggestionSettingsModelstores and persists the preference viaUserDefaults, threads it throughsnapshotandsnapshotPublisher, and upgrades the innerCombineLatesttoCombineLatest3to stay within Combine's 4-upstream cap.OverlayControllerswitches from a cached policy to a computedcurrentRenderModePolicythat reads the live setting on eachshowSuggestioncall, so changes take effect immediately without any subscription bookkeeping.mirrorPreferenceparameter so all existing call sites compile unchanged.Confidence Score: 4/5
Safe to merge; the default is
.autoso existing users are unaffected, and the new setting path is well-covered by tests.The change is additive and backwards-compatible. The one notable issue is that
helpDescriptionwas added toMirrorPreferencewith the apparent intent of using it as per-item tooltips, but the UI never reads it — both pickers use hardcoded.help(…)strings instead. This leaves the property as dead code, but it has no runtime impact.CompletionRenderModePolicy.swift— the unusedhelpDescriptionproperty warrants a quick follow-up (either wire it into the per-item.help()call or remove it).Important Files Changed
Identifiableconformance and per-casedisplayLabel/helpDescriptiontoMirrorPreference;helpDescriptionis dead code — never used by the UI.mirrorPreference: MirrorPreferencefield toSuggestionSettingsSnapshot; straightforward additive struct change.mirrorPreference@Publishedproperty, persistence viaUserDefaultsraw-value string,setMirrorPreferencemutator, and threads it through both thesnapshotgetter andsnapshotPublisher(upgrading the innerCombineLatesttoCombineLatest3). Logic is consistent and well-guarded.renderModePolicywith a computedcurrentRenderModePolicythat reads the livemirrorPreferenceon eachshowSuggestioncall; keeps the optional override seam for tests. Clean and correct.mirrorPreferenceBinding; mirrors the SettingsView implementation correctly.Bindingthat callssetMirrorPreference; well-placed and consistent with other settings rows.mirrorPreference: MirrorPreference = .autoto the shared snapshot fixture factory; all existing call sites pick it up transparently.runOnMainActor+ XCTestExpectation pattern correctly.Sequence Diagram
sequenceDiagram actor User participant Settings/MenuBar as Settings / MenuBar participant SettingsModel as SuggestionSettingsModel participant UserDefaults participant OverlayController participant Policy as CompletionRenderModePolicy User->>Settings/MenuBar: Select display preference (Auto/Inline/Popup) Settings/MenuBar->>SettingsModel: setMirrorPreference(_:) SettingsModel->>SettingsModel: "mirrorPreference = preference" SettingsModel->>UserDefaults: persist rawValue string SettingsModel-->>Settings/MenuBar: "@Published emits (UI updates)" Note over SettingsModel: snapshotPublisher picks up change via CombineLatest3 User->>OverlayController: (types, triggers suggestion) OverlayController->>OverlayController: currentRenderModePolicy (computed) OverlayController->>SettingsModel: read mirrorPreference live OverlayController->>Policy: mode(for: geometry, bundleIdentifier:) Policy-->>OverlayController: .inline or .mirror(reason:) OverlayController-->>User: Show ghost text or popup cardComments Outside Diff (1)
Cotabby/Support/CompletionRenderModePolicy.swift, line 33-45 (link)helpDescriptionis defined but never readThe
helpDescriptionproperty was added onMirrorPreferenceapparently for per-item tooltips, but neitherSettingsViewnorMenuBarViewreference it — both views attach a single hardcoded.help(…)string to the wholePicker. The property is unreachable dead code as shipped.Reviews (1): Last reviewed commit: "Let users toggle popup-card suggestion d..." | Re-trigger Greptile