-
Notifications
You must be signed in to change notification settings - Fork 33
feat: Add hybrid mode for compact content in expanded state #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dima6312
wants to merge
4
commits into
MrKai77:main
Choose a base branch
from
dima6312:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
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
* feat: add showCompactContentInExpandedMode for hybrid notch layouts Adds support for "hybrid" layouts where compact leading/trailing content remains visible while in expanded state. This enables use cases like dictation UIs with waveform indicators alongside the notch while transcript content shows below. Changes: - Add `showCompactContentInExpandedMode` property (defaults to false) - Add init parameter for convenience - Implement symmetric layout in NotchView for hybrid expanded mode - Add comprehensive documentation for layout logic - Add test cases for notch and floating styles Also fixes: - Prevent continuation leak in hide() when closePanelTask is cancelled 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * feat: Make NSScreen extensions public Expose `hasNotch`, `notchSize`, `notchFrame`, `menubarHeight`, and `screenWithMouse` for client apps that need to detect notch presence and adjust their behavior accordingly (e.g., always using expand() instead of compact() on non-notch Macs). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * feat: Auto-enable hybrid mode in floating style for UX consistency When compact() is called in floating mode, instead of hiding the window, expand with hybrid mode enabled. This ensures compact indicators (like waveforms, status icons, cancel buttons) remain visible on non-notch Macs, providing consistent UX across all Mac hardware. Previously, non-notch Mac users would not see compact content at all since floating mode has no physical notch to flank. Now they see the same indicators as notch Mac users, displayed alongside the expanded content. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: Add hybrid mode support for floating style fallback - Add internal floatingHybridModeActive flag to avoid mutating user property - Add isHybridModeEnabled computed property for unified hybrid mode check - Update compact() to auto-enable hybrid mode on floating style - Add compact indicators overlay to NotchlessView for floating panels - Fix animation when transitioning to hybrid mode from expanded state - Add test cases for hybrid mode and floating fallback behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * feat: Add compactCenterContent for floating fallback mode - Add compactCenterContent property for center UI in floating fallback - Add compactCenter case to DynamicNotchSection enum - Update NotchlessView to show indicators row with center content - Remove top inset gap when in hybrid mode - Reset floatingHybridModeActive on hide to prevent state persistence 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: smooth direct transition from expanded to compact state Remove intermediate hide step when transitioning from expanded to compact, eliminating the visual flash. Uses conversionAnimation for seamless morphing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: Prevent race condition when cancelling hide task Only call deinitializeWindow() if task wasn't cancelled, since a new window may have been opened by expand()/compact() after cancellation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: Await hybrid mode animation to prevent race with hide() Changed fire-and-forget Task to awaited MainActor.run and added sleep to wait for animation completion before compact() returns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * feat: make isHovering property public for client observation Allow clients to observe hover state changes via Combine to implement hover-based activation patterns (e.g., activate panel when mouse enters). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: comprehensive improvements for hybrid mode reliability Thread Safety & Race Conditions: - Use structured concurrency with MainActor.run instead of unstructured Tasks - Make state checks atomic in floating mode logic - Add task cancellation on all early return paths Memory Management: - Track observeScreenParameters Task and cancel in deinit - Use weak self capture to prevent retain cycles - Only reinitialize window when state is hidden Animation & State Transitions: - Extract animationDuration constant from DynamicNotchStyle - Make compact<->expanded transitions symmetric (both direct) - Reset floatingHybridModeActive even when already expanded - Move flag reset after animation completes to prevent visual glitch API Improvements: - Make floatingHybridModeActive private(set) - Optimize redundant hybrid mode animations - Add max retry count for hover-blocked hide operations View Fixes: - Constrain compact indicators row height in NotchlessView Tests: - Add comprehensive assertions for state verification - Add tests for flag resets, computed property logic, rapid transitions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Fix and docs: add hybrid mode documentation to README and DocC and fix code review issues (#2) * fix: preserve floatingHybridModeActive when compact() calls _expand() from hidden state The floatingHybridModeActive flag was being reset in _expand() before the guard check, which broke the floating fallback when compact() was called from .hidden state. The fix adds a resetHybridMode parameter to _expand() that defaults to true but is set to false when called from _compact(), preserving the hybrid mode flag set by the floating fallback logic. Also adds a test case to verify compact() works correctly from hidden state. * docs: add hybrid mode documentation to README and DocC Document the new hybrid mode feature: - README: Add Hybrid Mode section with code example and floating style behavior - DocC: Add Hybrid Mode section explaining the feature and automatic fallback * fix: improve floating hybrid mode reliability and documentation - Clarify README wording: indicators are "displayed when requested" not "always visible" - Fix icon sizing in floating hybrid mode by adding proper safeAreaInsets - Fix rapid state transition rendering by using opacity instead of conditional rendering - Add animation delay in test for state transition reliability - Apply swiftformat 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: prevent compact indicator layout compression during rapid transitions Add .fixedSize() and .layoutPriority(1) to compact leading/trailing content in floating view so SwiftUI preserves their intrinsic sizes even during rapid state changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude <[email protected]> * fix: improve code quality and accessibility - Remove unused skipHide parameter from _expand() and _compact() - Add .accessibilityHidden() to opacity-hidden views for VoiceOver - Fix rapid transition rendering by always rendering compact row (use frame height instead of conditional rendering to prevent SwiftUI view identity corruption) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: improve floating mode icon layout and animation smoothness - Apply animation at VStack level for smooth container resize when hiding top row - Use explicit frame constraints for compact icons instead of fixedSize() - Disable matchedGeometryEffect in hybrid mode to prevent icon animation conflicts - Forward objectWillChange from internal DynamicNotch to DynamicNotchInfo - Reset shouldSkipHideWhenConverting when compactLeading is explicitly changed - Animate floatingHybridModeActive reset with conversionAnimation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: improve code quality, fix race conditions, and refine test behavior - Fix race condition in closePanelTask that caused missing trailing icon on rapid transitions - Add explicit frame constraints for compact icons in NotchView (fixes gradient sizing in notch mode) - Update gradient test to skip compact() in floating mode (tested separately in hybrid mode tests) - Expose showCompactContentInExpandedMode parameter on DynamicNotchInfo - Remove duplicated doc comments in DynamicNotchStyle - Update comment to reflect actual purpose (removed inset references) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
Change frame constraints from fixed width to minWidth for compact leading/trailing content in both NotchView and NotchlessView. This allows text content like "Pasted" to display fully instead of being clipped to icon size. - NotchView: content expands away from notch (leading→left, trailing→right) - NotchlessView: add fixedSize() and layoutPriority(1) for layout stability - Guard compactIconSize with max(..., 0) to prevent negative values 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Reset namespace in deinitializeWindow() to prevent stale references - Switch to conditional rendering with transition for compact indicators row instead of height 0 + clipped approach which caused layout issues - Remove unnecessary fixedSize() and layoutPriority() modifiers The previous approach of always rendering the row with height 0 and clipped caused the trailing content to not render on subsequent window recreations during rapid state transitions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Make disableCompactLeading and disableCompactTrailing properties public so developers can dynamically hide compact views on DynamicNotch instances - Fix floating mode (NotchlessView) to use conditional rendering instead of opacity, ensuring disabled views don't reserve space - Use HStack spacing for cleaner layout when views are conditionally removed No breaking changes - existing DynamicNotchInfo API continues to work unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
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
This PR introduces hybrid mode - a layout option that displays compact leading/trailing content alongside the expanded notch panel. This enables use cases like dictation UIs with persistent waveform indicators, or media players with always-visible controls.
Key additions:
showCompactContentInExpandedModeproperty for hybrid layoutsNSScreenextensions for notch detection (hasNotch,notchSize,notchFrame)isHoveringproperty for hover-based activation patternsMotivation
When building interfaces like voice dictation UIs, developers need status indicators (waveforms, cancel buttons) that remain visible while expanded content displays below. Previously, compact content was only visible in
.compactstate on notch Macs and invisible entirely on non-notch Macs.This PR solves both:
Usage
On non-notch Macs,
compact()automatically enables hybrid mode in floating style.Changes
Features
showCompactContentInExpandedMode- compact content visible in expanded statecompactCenterContent- optional center content for floating hybrid modeNSScreenextensions for notch detectionisHoveringfor hover observationdisableCompactLeadinganddisableCompactTrailingproperties for dynamic compact view controlBug Fixes
hide()whenclosePanelTaskis cancelledCode Quality
MainActor.runanimationDurationconstant extracted fromDynamicNotchStyle.accessibilityHidden()for VoiceOver)Test Plan
Breaking Changes
None. All APIs are additive with sensible defaults.
Demo