├── .gitignore ├── DynamicIsland.xcodeproj ├── project.pbxproj ├── project.pbxproj.backup ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcuserdata │ └── admin63.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── DynamicIsland ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── notch-stage-icon2 10.png │ │ ├── notch-stage-icon2 9.png │ │ ├── upscaled-2.png │ │ ├── upscaled-3 1.png │ │ ├── upscaled-3.png │ │ ├── upscaled-4.png │ │ ├── upscaled-5.png │ │ ├── upscaled-6 1.png │ │ ├── upscaled-6.png │ │ └── upscaled-7.png │ ├── Contents.json │ ├── Github.imageset │ │ ├── Contents.json │ │ ├── GitHub Mark White 1.svg │ │ ├── GitHub Mark White 2.svg │ │ └── GitHub Mark White.svg │ ├── LinkedIn.imageset │ │ ├── Contents.json │ │ └── LinkedIn_logo_initials.png │ ├── bolt.imageset │ │ ├── Contents.json │ │ └── bolt.png │ ├── chrome.imageset │ │ ├── Contents.json │ │ ├── Google Chrome macOS BigSur Icon 1.png │ │ ├── Google Chrome macOS BigSur Icon 2.png │ │ └── Google Chrome macOS BigSur Icon.png │ ├── defaultmusic.imageset │ │ └── Contents.json │ ├── ebullioscopic.imageset │ │ ├── 819b59197be07c800478c728143326f1.png │ │ └── Contents.json │ ├── logo.imageset │ │ ├── ChatGPT Image Aug 13, 2025, 11_50_32 PM.png │ │ └── Contents.json │ ├── logo2.imageset │ │ ├── ChatGPT_Image_Aug_13__2025__11_50_32_PM-removebg-preview.png │ │ └── Contents.json │ ├── plug.imageset │ │ ├── Contents.json │ │ └── plug.png │ ├── sparkle.imageset │ │ ├── Contents.json │ │ └── sparkle.svg │ └── spotlight.imageset │ │ ├── Contents.json │ │ └── spotlight.svg ├── ContentView.swift ├── DynamicIsland.entitlements ├── DynamicIslandApp.swift ├── DynamicIslandViewCoordinator.swift ├── Info.plist ├── Localizable.xcstrings ├── Localizable.xcstrings.backup ├── MediaControllers │ ├── AppleMusicController.swift │ ├── MediaControllerProtocol.swift │ ├── NowPlayingController.swift │ ├── SpotifyController.swift │ └── YouTube Music Controller │ │ ├── YouTubeMusicAuthentication.swift │ │ ├── YouTubeMusicController.swift │ │ ├── YouTubeMusicModels.swift │ │ └── YouTubeMusicNetworking.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Providers │ └── CalendarServiceProviding.swift ├── Shortcuts │ └── ShortcutConstants.swift ├── animations │ ├── HelloAnimation.swift │ └── drop.swift ├── components │ ├── AnimatedFace.swift │ ├── BottomRoundedRectangle.swift │ ├── Calendar │ │ └── DynamicIslandCalendar.swift │ ├── Clipboard │ │ ├── ClipboardPanel.swift │ │ ├── ClipboardPopover.swift │ │ ├── ClipboardShared.swift │ │ └── ClipboardWindow.swift │ ├── ColorCodedProgressBar.swift │ ├── ColorPicker │ │ ├── ColorPickedFeedbackView.swift │ │ ├── ColorPickerPanel.swift │ │ └── ColorPickerPopover.swift │ ├── DynamicIslandSystemTiles.swift │ ├── EmptyState.swift │ ├── HoverButton.swift │ ├── Live activities │ │ ├── DownloadView.swift │ │ ├── DynamicIslandBattery.swift │ │ ├── InlineHUD.swift │ │ ├── LiveActivityModifier.swift │ │ ├── MarqueeTextView.swift │ │ └── SystemEventIndicatorModifier.swift │ ├── MinimalisticMusicView.swift │ ├── Music │ │ ├── LottieAnimationView.swift │ │ └── MusicVisualizer.swift │ ├── Notch │ │ ├── ClipboardHistoryPopover.swift │ │ ├── DynamicIslandExtrasMenu.swift │ │ ├── DynamicIslandHeader.swift │ │ ├── DynamicIslandWindow.swift │ │ ├── MinimalisticMusicPlayerView.swift │ │ ├── NotchColorPickerView.swift │ │ ├── NotchHomeView.swift │ │ ├── NotchShape.swift │ │ ├── NotchShelfView.swift │ │ ├── NotchStatsView.swift │ │ └── NotchTimerView.swift │ ├── Onboarding │ │ ├── ActivationView.swift │ │ ├── MusicControllerSelectionView.swift │ │ ├── OnboardingFinishView.swift │ │ ├── OnboardingView.swift │ │ ├── PermissionsRequestView.swift │ │ ├── ProOnboarding.swift │ │ ├── ProfileSelectionView.swift │ │ ├── SparkleView.swift │ │ └── WelcomeView.swift │ ├── ProgressIndicator.swift │ ├── Recording │ │ └── RecordingLiveActivity.swift │ ├── ScreenAssistant │ │ ├── ChatPanels.swift │ │ ├── ModelSelectionPanel.swift │ │ ├── ScreenAssistantPanel.swift │ │ └── ScreenshotSnippingTool.swift │ ├── Settings │ │ ├── EditPanelView.swift │ │ ├── ListItemPopover.swift │ │ ├── SettingsView.swift │ │ ├── SettingsWindowController.swift │ │ └── SoftwareUpdater.swift │ ├── Shelf │ │ ├── AirDrop.swift │ │ ├── AirDropView.swift │ │ ├── DragDropView.swift │ │ ├── DropItem.swift │ │ ├── DropItemView.swift │ │ ├── Ext+FileProvider.swift │ │ ├── Ext+NSAlert.swift │ │ ├── Ext+NSImage.swift │ │ ├── Ext+URL.swift │ │ └── TrayDrop.swift │ ├── Stats │ │ ├── DetailedTimelineGraph.swift │ │ ├── DualTimelineGraph.swift │ │ ├── StatsPanel.swift │ │ └── StatsPanelView.swift │ ├── Tabs │ │ ├── TabButton.swift │ │ └── TabSelectionView.swift │ ├── TestView.swift │ ├── Timer │ │ ├── TimerIconAnimation.swift │ │ └── TimerLiveActivity.swift │ ├── Tips │ │ └── TipStore.swift │ ├── UI │ │ └── RecordingIndicator.swift │ ├── Webcam │ │ └── WebcamView.swift │ └── WhatsNewView.swift ├── dynamic.m4a ├── enums │ └── generic.swift ├── extensions │ ├── ActionBar.swift │ ├── BundleInfos.swift │ ├── Button+Bouncing.swift │ ├── ConditionalModifier.swift │ ├── DataTypes+Extensions.swift │ ├── KeyboardShortcutsHelper.swift │ ├── MouseTracker.swift │ ├── NSImage+Extensions.swift │ └── PanGesture.swift ├── helpers │ ├── AppIcons.swift │ ├── AppleScriptError.swift │ ├── AppleScriptHelper.swift │ ├── AppleScriptRunner.swift │ ├── AudioPlayer.swift │ ├── Clipboard+Content.swift │ ├── MediaChecker.swift │ ├── SensorError.swift │ ├── SensorMethod.swift │ └── SystemHUDDebugger.swift ├── managers │ ├── BatteryActivityManager.swift │ ├── BluetoothAudioManager.swift │ ├── CalendarManager.swift │ ├── ClipboardManager.swift │ ├── ClipboardPanelManager.swift │ ├── ClipboardWindowManager.swift │ ├── ColorPickerManager.swift │ ├── ColorPickerManager_Fixed.swift │ ├── DoNotDisturbManager.swift │ ├── DynamicIslandExtensionManager.swift │ ├── ImageService.swift │ ├── MusicManager.swift │ ├── NotchSpaceManager.swift │ ├── ScreenAssistantManager.swift │ ├── ScreenAssistantPanelManager.swift │ ├── ScreenRecordingManager.swift │ ├── StatsManager.swift │ ├── StatsPanelManager.swift │ ├── SystemChangesObserver.swift │ ├── SystemDisplayManager.swift │ ├── SystemHUDManager.swift │ ├── SystemKeyObserver.swift │ ├── SystemOSDManager.swift │ ├── SystemVolumeManager.swift │ ├── TimerManager.swift │ └── WebcamManager.swift ├── menu │ └── StatusBarMenu.swift ├── metal │ └── visualizer.metal ├── models │ ├── BatteryStatusViewModel.swift │ ├── CalendarModel.swift │ ├── Constants.swift │ ├── DynamicIslandViewModel.swift │ ├── EventModel.swift │ ├── PickedColor.swift │ └── PlaybackState.swift ├── observers │ └── FullscreenMediaDetection.swift ├── private │ └── CGSSpace.swift ├── sizing │ └── matters.swift ├── strings │ └── constants.swift └── utils │ └── Logger.swift ├── DynamicIslandSamples ├── clipboardpanel.png ├── clipboardpopover.png ├── colorpickerpanel.png ├── colorpickerpopover.png ├── dynamicisland-minimalistic.png ├── dynamicislandscreenrecord.gif ├── logo.png ├── media.png └── statsmonitor.png ├── Frameworks └── MediaRemoteAdapter.framework │ ├── MediaRemoteAdapter │ ├── Resources │ └── Versions │ ├── A │ ├── MediaRemoteAdapter │ ├── Resources │ │ └── Info.plist │ └── _CodeSignature │ │ └── CodeResources │ └── Current ├── LICENSE ├── MIGRATION_SUMMARY.md ├── ONBOARDING_ENHANCEMENT.md ├── ReadMe.md ├── Updates └── appcast.xml └── mediaremote-adapter ├── MediaRemoteAdapter.framework ├── MediaRemoteAdapter ├── Resources └── Versions │ ├── A │ ├── MediaRemoteAdapter │ ├── Resources │ │ └── Info.plist │ └── _CodeSignature │ │ └── CodeResources │ └── Current ├── NowPlayingTestClient └── mediaremote-adapter.pl /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Xcode 3 | # 4 | build/ 5 | DerivedData/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xcscmblueprint 16 | 17 | # SwiftPM 18 | # 19 | .build/ 20 | Package.resolved 21 | 22 | # CocoaPods 23 | # 24 | Pods/ 25 | Podfile.lock 26 | 27 | # Carthage 28 | # 29 | Carthage/Build/ 30 | 31 | # Fastlane 32 | # 33 | fastlane/report.xml 34 | fastlane/Preview.html 35 | fastlane/screenshots 36 | fastlane/test_output 37 | 38 | # Archives 39 | # 40 | *.xcarchive 41 | 42 | # App data 43 | # 44 | *.ipa 45 | *.dSYM.zip 46 | *.dSYM 47 | 48 | # Playgrounds 49 | # 50 | timeline.xctimeline 51 | playground.xcworkspace 52 | 53 | # Xcode Server 54 | # 55 | integration.json 56 | 57 | # Other 58 | # 59 | *.moved-aside 60 | *.xcuserstate 61 | 62 | # GitHub instructions and agent files 63 | # 64 | .github/ 65 | rough_note.md 66 | Solid 67 | SettingsView.swift.md 68 | logic.md 69 | TheBoringWorker-HUD 70 | boringnotch + meteo + airpods + traduzione modificabile 71 | segmentedhud.swift.ref 72 | gemini-apireference.md 73 | 74 | ScreenshotApp-main 75 | stats 76 | boring.notch 77 | referenceimages 78 | 79 | PrivateWorks 80 | 81 | *.py 82 | *.sh 83 | comparison_report.json 84 | *.txt -------------------------------------------------------------------------------- /DynamicIsland.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DynamicIsland.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "2741287b65cf0758abd89baadf314f5f8cbfcb46cb8d236a83f98731e5501ee3", 3 | "pins" : [ 4 | { 5 | "identity" : "defaults", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/sindresorhus/Defaults", 8 | "state" : { 9 | "revision" : "00c82eff4550c87cf9c547d7e5493a6a97837061", 10 | "version" : "9.0.3" 11 | } 12 | }, 13 | { 14 | "identity" : "keyboardshortcuts", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/sindresorhus/KeyboardShortcuts", 17 | "state" : { 18 | "revision" : "045cf174010beb335fa1d2567d18c057b8787165", 19 | "version" : "2.3.0" 20 | } 21 | }, 22 | { 23 | "identity" : "launchatlogin-modern", 24 | "kind" : "remoteSourceControl", 25 | "location" : "https://github.com/sindresorhus/LaunchAtLogin-Modern", 26 | "state" : { 27 | "branch" : "main", 28 | "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc" 29 | } 30 | }, 31 | { 32 | "identity" : "lottie-spm", 33 | "kind" : "remoteSourceControl", 34 | "location" : "https://github.com/airbnb/lottie-spm.git", 35 | "state" : { 36 | "branch" : "main", 37 | "revision" : "90fa25ba0feb39c22915d41b55226cc95955dfcc" 38 | } 39 | }, 40 | { 41 | "identity" : "lottieui", 42 | "kind" : "remoteSourceControl", 43 | "location" : "https://github.com/jasudev/LottieUI.git", 44 | "state" : { 45 | "branch" : "main", 46 | "revision" : "0cd5b54a1c8467b19c01f56395aec179087b62e2" 47 | } 48 | }, 49 | { 50 | "identity" : "macrovisionkit", 51 | "kind" : "remoteSourceControl", 52 | "location" : "https://github.com/TheBoredTeam/MacroVisionKit", 53 | "state" : { 54 | "revision" : "5e8b06c448298de182638bb28ca064eaf6b1fe99", 55 | "version" : "0.1.0" 56 | } 57 | }, 58 | { 59 | "identity" : "pow", 60 | "kind" : "remoteSourceControl", 61 | "location" : "https://github.com/EmergeTools/Pow", 62 | "state" : { 63 | "revision" : "a504eb6d144bcf49f4f33029a2795345cb39e6b4", 64 | "version" : "1.0.5" 65 | } 66 | }, 67 | { 68 | "identity" : "sparkle", 69 | "kind" : "remoteSourceControl", 70 | "location" : "https://github.com/sparkle-project/Sparkle", 71 | "state" : { 72 | "revision" : "0ca3004e98712ea2b39dd881d28448630cce1c99", 73 | "version" : "2.7.0" 74 | } 75 | }, 76 | { 77 | "identity" : "swift-collections", 78 | "kind" : "remoteSourceControl", 79 | "location" : "https://github.com/apple/swift-collections.git", 80 | "state" : { 81 | "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", 82 | "version" : "1.1.4" 83 | } 84 | }, 85 | { 86 | "identity" : "swift-syntax", 87 | "kind" : "remoteSourceControl", 88 | "location" : "https://github.com/swiftlang/swift-syntax", 89 | "state" : { 90 | "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", 91 | "version" : "601.0.1" 92 | } 93 | }, 94 | { 95 | "identity" : "swiftui-introspect", 96 | "kind" : "remoteSourceControl", 97 | "location" : "https://github.com/siteline/swiftui-introspect", 98 | "state" : { 99 | "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336", 100 | "version" : "1.3.0" 101 | } 102 | }, 103 | { 104 | "identity" : "theboringworkernotifier", 105 | "kind" : "remoteSourceControl", 106 | "location" : "https://github.com/TheBoredTeam/TheBoringWorkerNotifier.git", 107 | "state" : { 108 | "branch" : "main", 109 | "revision" : "bc2b292c88035dcfc41fc7e3d8021689e624d137" 110 | } 111 | } 112 | ], 113 | "version" : 3 114 | } 115 | -------------------------------------------------------------------------------- /DynamicIsland.xcodeproj/xcuserdata/admin63.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DynamicIsland.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "upscaled-7.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "upscaled-6 1.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "upscaled-6.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "upscaled-5.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "upscaled-4.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "upscaled-3 1.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "upscaled-3.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "upscaled-2.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "notch-stage-icon2 9.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "notch-stage-icon2 10.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/notch-stage-icon2 10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/notch-stage-icon2 10.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/notch-stage-icon2 9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/notch-stage-icon2 9.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-2.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-3 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-3 1.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-3.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-4.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-5.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-6 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-6 1.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-6.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/AppIcon.appiconset/upscaled-7.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/Github.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "GitHub Mark White.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "GitHub Mark White 1.svg", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "GitHub Mark White 2.svg", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/Github.imageset/GitHub Mark White 1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/Github.imageset/GitHub Mark White 2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/Github.imageset/GitHub Mark White.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/LinkedIn.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LinkedIn_logo_initials.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/LinkedIn.imageset/LinkedIn_logo_initials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/LinkedIn.imageset/LinkedIn_logo_initials.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/bolt.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "bolt.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/bolt.imageset/bolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/bolt.imageset/bolt.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/chrome.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Google Chrome macOS BigSur Icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Google Chrome macOS BigSur Icon 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Google Chrome macOS BigSur Icon 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon 1.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon 2.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/chrome.imageset/Google Chrome macOS BigSur Icon.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/defaultmusic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "3x" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/ebullioscopic.imageset/819b59197be07c800478c728143326f1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/ebullioscopic.imageset/819b59197be07c800478c728143326f1.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/ebullioscopic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "819b59197be07c800478c728143326f1.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/logo.imageset/ChatGPT Image Aug 13, 2025, 11_50_32 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/logo.imageset/ChatGPT Image Aug 13, 2025, 11_50_32 PM.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ChatGPT Image Aug 13, 2025, 11_50_32 PM.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/logo2.imageset/ChatGPT_Image_Aug_13__2025__11_50_32_PM-removebg-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/logo2.imageset/ChatGPT_Image_Aug_13__2025__11_50_32_PM-removebg-preview.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/logo2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ChatGPT_Image_Aug_13__2025__11_50_32_PM-removebg-preview.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/plug.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "plug.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/plug.imageset/plug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/Assets.xcassets/plug.imageset/plug.png -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/sparkle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "sparkle.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/sparkle.imageset/sparkle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/spotlight.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "spotlight.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicIsland/Assets.xcassets/spotlight.imageset/spotlight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /DynamicIsland/DynamicIsland.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | com.apple.security.temporary-exception.apple-events 8 | 9 | com.spotify.client 10 | com.apple.Music 11 | 12 | com.apple.security.temporary-exception.mach-lookup.global-name 13 | 14 | $(PRODUCT_BUNDLE_IDENTIFIER)-spks 15 | $(PRODUCT_BUNDLE_IDENTIFIER)-spki 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /DynamicIsland/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | NSBluetoothAlwaysUsageDescription 11 | Dynamic Island needs Bluetooth access to detect when audio devices connect and display their battery status in the HUD. 12 | NSScreenCaptureUsageDescription 13 | Dynamic Island needs screen recording permission to capture screenshots for the AI assistant feature. 14 | SUEnableDownloaderService 15 | 16 | SUEnableInstallerLauncherService 17 | 18 | SUFeedURL 19 | https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/main/Updates/appcast.xml 20 | SUPublicEDKey 21 | q2YQaJ1umGkaIJWMGN9Isj5fx/YlUtxnzHEBqFtfZcg= 22 | UTImportedTypeDeclarations 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /DynamicIsland/MediaControllers/MediaControllerProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MediaControllerProtocol.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander Greco on 2025-03-29. 6 | // 7 | 8 | import Foundation 9 | import AppKit 10 | import Combine 11 | 12 | protocol MediaControllerProtocol: ObservableObject { 13 | var playbackStatePublisher: AnyPublisher { get } 14 | var isWorking: Bool { get } 15 | func play() async 16 | func pause() async 17 | func seek(to time: Double) async 18 | func nextTrack() async 19 | func previousTrack() async 20 | func togglePlay() async 21 | func toggleShuffle() async 22 | func toggleRepeat() async 23 | func isActive() -> Bool 24 | func updatePlaybackInfo() async 25 | } 26 | -------------------------------------------------------------------------------- /DynamicIsland/MediaControllers/YouTube Music Controller/YouTubeMusicAuthentication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YouTubeMusicAuthentication.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-09-14. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Authentication Manager 11 | actor YouTubeMusicAuthManager { 12 | private var accessToken: String? 13 | private var authenticationTask: Task? 14 | private let httpClient: YouTubeMusicHTTPClient 15 | 16 | init(httpClient: YouTubeMusicHTTPClient) { 17 | self.httpClient = httpClient 18 | } 19 | 20 | var currentToken: String? { 21 | accessToken 22 | } 23 | 24 | func authenticate() async throws -> String { 25 | // Return existing token if valid 26 | if let token = accessToken { 27 | return token 28 | } 29 | 30 | // Wait for ongoing authentication if in progress 31 | if let task = authenticationTask { 32 | return try await task.value 33 | } 34 | 35 | // Start new authentication 36 | let task = Task { 37 | do { 38 | let token = try await httpClient.authenticate() 39 | await setToken(token) 40 | return token 41 | } catch { 42 | await clearAuthenticationTask() 43 | throw error 44 | } 45 | } 46 | 47 | authenticationTask = task 48 | return try await task.value 49 | } 50 | 51 | func invalidateToken() async { 52 | accessToken = nil 53 | authenticationTask?.cancel() 54 | authenticationTask = nil 55 | } 56 | 57 | private func setToken(_ token: String) async { 58 | accessToken = token 59 | authenticationTask = nil 60 | } 61 | 62 | private func clearAuthenticationTask() async { 63 | authenticationTask = nil 64 | } 65 | } 66 | 67 | // MARK: - Authentication State 68 | enum AuthenticationState: Sendable { 69 | case unauthenticated 70 | case authenticating 71 | case authenticated(String) 72 | case failed(Error) 73 | 74 | var isAuthenticated: Bool { 75 | if case .authenticated = self { 76 | return true 77 | } 78 | return false 79 | } 80 | 81 | var token: String? { 82 | if case .authenticated(let token) = self { 83 | return token 84 | } 85 | return nil 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /DynamicIsland/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /DynamicIsland/Shortcuts/ShortcutConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 16/08/2024. 6 | // 7 | 8 | import KeyboardShortcuts 9 | import SwiftUI 10 | 11 | extension KeyboardShortcuts.Name { 12 | static let clipboardHistoryPanel = Self("clipboardHistoryPanel", default: .init(.c, modifiers: [.shift, .command])) 13 | static let colorPickerPanel = Self("colorPickerPanel", default: .init(.p, modifiers: [.shift, .command])) 14 | static let screenAssistantPanel = Self("screenAssistantPanel", default: .init(.a, modifiers: [.shift, .command])) 15 | static let statsPanel = Self("statsPanel", default: .init(.s, modifiers: [.shift, .command])) 16 | static let toggleMicrophone = Self("toggleMicrophone", default: .init(.f5, modifiers: [.function])) 17 | static let decreaseBacklight = Self("decreaseBacklight", default: .init(.f1, modifiers: [.command])) 18 | static let increaseBacklight = Self("increaseBacklight", default: .init(.f2, modifiers: [.command])) 19 | static let toggleSneakPeek = Self("toggleSneakPeek", default: .init(.h, modifiers: [.command, .shift])) 20 | static let toggleNotchOpen = Self("toggleNotchOpen", default: .init(.i, modifiers: [.command, .shift])) 21 | static let startDemoTimer = Self("startDemoTimer", default: .init(.t, modifiers: [.command, .shift])) 22 | } 23 | -------------------------------------------------------------------------------- /DynamicIsland/animations/drop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // drop.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | 12 | public class DynamicIslandAnimations { 13 | @Published var notchStyle: Style = .notch 14 | 15 | init() { 16 | self.notchStyle = .notch 17 | } 18 | 19 | var animation: Animation { 20 | if #available(macOS 14.0, *), notchStyle == .notch { 21 | Animation.spring(.bouncy(duration: 0.4)) 22 | } else { 23 | Animation.timingCurve(0.16, 1, 0.3, 1, duration: 0.7) 24 | } 25 | } 26 | 27 | // TODO: Move all animations to this file 28 | 29 | } 30 | -------------------------------------------------------------------------------- /DynamicIsland/components/AnimatedFace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimatedFace.swift 3 | // 4 | // Created by Harsh Vardhan Goswami on 04/08/24. 5 | // 6 | 7 | import SwiftUI 8 | 9 | struct MinimalFaceFeatures: View { 10 | @State private var isBlinking = false 11 | @State var height:CGFloat = 20; 12 | @State var width:CGFloat = 30; 13 | 14 | var body: some View { 15 | VStack(spacing: 4) { // Adjusted spacing to fit within 30x30 16 | // Eyes 17 | HStack(spacing: 4) { // Adjusted spacing to fit within 30x30 18 | Eye(isBlinking: $isBlinking) 19 | Eye(isBlinking: $isBlinking) 20 | } 21 | 22 | // Nose and mouth combined 23 | VStack(spacing: 2) { // Adjusted spacing to fit within 30x30 24 | // Nose 25 | RoundedRectangle(cornerRadius: 2) 26 | .fill(Color.white) 27 | .frame(width: 3, height: 4) 28 | 29 | // Mouth (happy) 30 | GeometryReader { geometry in 31 | Path { path in 32 | let width = geometry.size.width 33 | let height = geometry.size.height 34 | path.move(to: CGPoint(x: 0, y: height / 2)) 35 | path.addQuadCurve(to: CGPoint(x: width, y: height / 2), control: CGPoint(x: width / 2, y: height)) 36 | } 37 | .stroke(Color.white, lineWidth: 2) 38 | } 39 | .frame(width: 14, height: 10) 40 | } 41 | } 42 | .frame(width: self.width, height: self.height) // Maximum size of face 43 | .onAppear { 44 | startBlinking() 45 | } 46 | } 47 | 48 | func startBlinking() { 49 | Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in 50 | withAnimation(.spring(duration: 0.2)) { 51 | isBlinking = true 52 | } 53 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 54 | withAnimation(.spring(duration: 0.2)) { 55 | isBlinking = false 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | struct Eye: View { 63 | @Binding var isBlinking: Bool 64 | 65 | var body: some View { 66 | RoundedRectangle(cornerRadius: 10) 67 | .fill(Color.white) 68 | .frame(width: 4, height: isBlinking ? 1 : 4) 69 | .frame(maxWidth: 15, maxHeight: 15) // Adjusted max size 70 | .animation(.easeInOut(duration: 0.1), value: isBlinking) 71 | } 72 | } 73 | 74 | struct MinimalFaceFeatures_Previews: PreviewProvider { 75 | static var previews: some View { 76 | ZStack { 77 | Color.black 78 | MinimalFaceFeatures() 79 | } 80 | .previewLayout(.fixed(width: 60, height: 60)) // Adjusted preview size for better visibility 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /DynamicIsland/components/BottomRoundedRectangle.swift: -------------------------------------------------------------------------------- 1 | 2 | 3 | import SwiftUI 4 | 5 | 6 | struct BottomRoundedRectangle: Shape { 7 | var radius: CGFloat 8 | 9 | func path(in rect: CGRect) -> Path { 10 | var path = Path() 11 | 12 | // Top left corner 13 | path.move(to: CGPoint(x: rect.minX, y: rect.minY)) 14 | 15 | // Top right corner 16 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY)) 17 | 18 | // Bottom right corner (rounded) 19 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - radius)) 20 | path.addArc(center: CGPoint(x: rect.maxX - radius, y: rect.maxY - radius), 21 | radius: radius, 22 | startAngle: Angle(degrees: 0), 23 | endAngle: Angle(degrees: 90), 24 | clockwise: false) 25 | 26 | // Bottom left corner (rounded) 27 | path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY)) 28 | path.addArc(center: CGPoint(x: rect.minX + radius, y: rect.maxY - radius), 29 | radius: radius, 30 | startAngle: Angle(degrees: 90), 31 | endAngle: Angle(degrees: 180), 32 | clockwise: false) 33 | 34 | // Back to top left to close the path 35 | path.closeSubpath() 36 | 37 | return path 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /DynamicIsland/components/Clipboard/ClipboardShared.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClipboardShared.swift 3 | // DynamicIsland 4 | // 5 | // Created by Ebullioscopic on 12/08/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum ClipboardTab: String, CaseIterable { 11 | case history = "History" 12 | case favorites = "Favorites" 13 | 14 | var icon: String { 15 | switch self { 16 | case .history: return "clock" 17 | case .favorites: return "heart.fill" 18 | } 19 | } 20 | } 21 | 22 | struct ClipboardTabButton: View { 23 | let tab: ClipboardTab 24 | let isSelected: Bool 25 | let action: () -> Void 26 | @ObservedObject var clipboardManager = ClipboardManager.shared 27 | 28 | var itemCount: Int { 29 | switch tab { 30 | case .history: 31 | return clipboardManager.regularHistory.count 32 | case .favorites: 33 | return clipboardManager.pinnedItems.count 34 | } 35 | } 36 | 37 | var body: some View { 38 | Button(action: action) { 39 | HStack(spacing: 6) { 40 | Image(systemName: tab.icon) 41 | .font(.system(size: 11)) 42 | 43 | Text(tab.rawValue) 44 | .font(.system(size: 11, weight: .medium)) 45 | 46 | if itemCount > 0 { 47 | Text("\(itemCount)") 48 | .font(.system(size: 9, weight: .bold)) 49 | .foregroundColor(isSelected ? .white : .secondary) 50 | .padding(.horizontal, 4) 51 | .padding(.vertical, 1) 52 | .background( 53 | Capsule() 54 | .fill(isSelected ? Color.white.opacity(0.3) : Color.gray.opacity(0.2)) 55 | ) 56 | } 57 | } 58 | .foregroundColor(isSelected ? .white : .secondary) 59 | .padding(.horizontal, 12) 60 | .padding(.vertical, 6) 61 | .background( 62 | RoundedRectangle(cornerRadius: 6) 63 | .fill(isSelected ? Color.blue : Color.clear) 64 | ) 65 | } 66 | .buttonStyle(PlainButtonStyle()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DynamicIsland/components/ColorPicker/ColorPickedFeedbackView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorPickedFeedbackView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Ebullioscopic on 14/08/25. 6 | // 7 | 8 | import SwiftUI 9 | import Defaults 10 | 11 | struct ColorPickedFeedbackView: View { 12 | let color: PickedColor 13 | @Binding var isShowing: Bool 14 | 15 | var body: some View { 16 | VStack(spacing: 12) { 17 | // Color preview 18 | RoundedRectangle(cornerRadius: 12) 19 | .fill(color.color) 20 | .frame(width: 60, height: 60) 21 | .overlay( 22 | RoundedRectangle(cornerRadius: 12) 23 | .stroke(Color.white.opacity(0.8), lineWidth: 2) 24 | ) 25 | .shadow(color: .black.opacity(0.3), radius: 8) 26 | 27 | // Color info 28 | VStack(spacing: 4) { 29 | Text("Color Picked!") 30 | .font(.system(size: 14, weight: .semibold)) 31 | .foregroundColor(.primary) 32 | 33 | Text(color.hexString) 34 | .font(.system(size: 12, weight: .medium, design: .monospaced)) 35 | .foregroundColor(.secondary) 36 | .padding(.horizontal, 8) 37 | .padding(.vertical, 4) 38 | .background(Color.gray.opacity(0.2)) 39 | .cornerRadius(6) 40 | } 41 | } 42 | .padding(16) 43 | .background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)) 44 | .cornerRadius(16) 45 | .shadow(color: .black.opacity(0.3), radius: 12) 46 | .scaleEffect(isShowing ? 1.0 : 0.8) 47 | .opacity(isShowing ? 1.0 : 0.0) 48 | .animation(.spring(response: 0.4, dampingFraction: 0.7), value: isShowing) 49 | .onTapGesture { 50 | // Copy to clipboard on tap 51 | ColorPickerManager.shared.copyToClipboard(color.hexString) 52 | 53 | if Defaults[.enableHaptics] { 54 | NSHapticFeedbackManager.defaultPerformer.perform(.generic, performanceTime: .default) 55 | } 56 | } 57 | } 58 | } 59 | 60 | #Preview { 61 | ColorPickedFeedbackView( 62 | color: PickedColor(nsColor: NSColor.blue, point: CGPoint(x: 100, y: 100)), 63 | isShowing: .constant(true) 64 | ) 65 | .frame(width: 300, height: 200) 66 | } 67 | -------------------------------------------------------------------------------- /DynamicIsland/components/DynamicIslandSystemTiles.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicIslandSystemTiles.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // DynamicIslandSystemTiles.swift 7 | // DynamicIsland 8 | // 9 | // Created by Harsh Vardhan Goswami on 16/08/24. 10 | // 11 | 12 | import Foundation 13 | import SwiftUI 14 | import Defaults 15 | 16 | struct SystemItemButton: View { 17 | @EnvironmentObject var vm: DynamicIslandViewModel 18 | @State var icon: String = "gear" 19 | var onTap: () -> Void 20 | @State var label: String? 21 | @State var showEmojis: Bool = true 22 | @State var emoji: String = "🔧" 23 | 24 | var body: some View { 25 | Button(action: onTap) { 26 | if Defaults[.tileShowLabels] { 27 | HStack { 28 | if !showEmojis { 29 | Image(systemName: icon) 30 | .resizable() 31 | .aspectRatio(contentMode: .fit) 32 | .frame(width: 10) 33 | .foregroundStyle(.gray) 34 | } 35 | 36 | Text((showEmojis ? "\(emoji) " : "") + label!) 37 | .font(.caption2) 38 | .fontWeight(.regular) 39 | .foregroundStyle(.gray) 40 | .frame(maxWidth: .infinity, alignment: .leading) 41 | .allowsTightening(true) 42 | .minimumScaleFactor(0.7) 43 | .lineLimit(1) 44 | } 45 | } else { 46 | Color.clear 47 | .overlay { 48 | Image(systemName: icon) 49 | .foregroundStyle(.gray) 50 | } 51 | .aspectRatio(1, contentMode: .fit) 52 | } 53 | } 54 | .buttonStyle(BouncingButtonStyle(vm: vm)) 55 | } 56 | } 57 | 58 | func logout() { 59 | DispatchQueue.global(qos: .background).async { 60 | let appleScript = """ 61 | tell application "System Events" to log out 62 | """ 63 | 64 | var error: NSDictionary? 65 | if let scriptObject = NSAppleScript(source: appleScript) { 66 | scriptObject.executeAndReturnError(&error) 67 | if let error = error { 68 | print("Error: \(error)") 69 | } 70 | } 71 | } 72 | } 73 | 74 | struct DynamicIslandSystemTiles: View { 75 | @EnvironmentObject var vm: DynamicIslandViewModel 76 | @ObservedObject var coordinator = DynamicIslandViewCoordinator.shared 77 | 78 | struct ItemButton { 79 | var icon: String 80 | var onTap: () -> Void 81 | } 82 | 83 | var body: some View { 84 | Grid { 85 | GridRow { 86 | // SystemItemButton(icon: "clipboard", onTap: { 87 | // vm.openClipboard() 88 | // }, label: "Clipboard History", showEmojis: Defaults[.showEmojis], emoji: "✨") 89 | // SystemItemButton(icon: "keyboard", onTap: { 90 | // vm?.close() 91 | // vm?.togglesneakPeek(status: true, type: .backlight, value: 1) 92 | // }, label: "💡 Keyboard Backlight") 93 | } 94 | GridRow { 95 | SystemItemButton(icon: coordinator.currentMicStatus ? "mic" : "mic.slash", onTap: { 96 | coordinator.toggleMic() 97 | vm.close() 98 | }, label: "Toggle Microphone", showEmojis: Defaults[.showEmojis], emoji: coordinator.currentMicStatus ? "😀" : "🤫") 99 | // SystemItemButton(icon: "lock", onTap: { 100 | // logout() 101 | // }, label: "🔒 Lock My Device") 102 | } 103 | } 104 | } 105 | } 106 | 107 | #Preview { 108 | DynamicIslandSystemTiles().padding() 109 | } 110 | -------------------------------------------------------------------------------- /DynamicIsland/components/EmptyState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyState.swift 3 | // 4 | // Created by Harsh Vardhan Goswami on 04/08/24. 5 | // 6 | 7 | import SwiftUI 8 | 9 | struct EmptyStateView: View { 10 | var message: String 11 | @State private var isVisible = true 12 | 13 | var body: some View { 14 | HStack { 15 | MinimalFaceFeatures( 16 | height: 70, width: 80) 17 | Text(message) 18 | .font(.system(size:14)) 19 | .foregroundColor(.gray) 20 | }.transition(.blurReplace.animation(.spring(.bouncy(duration: 0.3)))) // Smooth animation 21 | } 22 | } 23 | 24 | #Preview { 25 | EmptyStateView(message: "Play some music babies") 26 | } 27 | -------------------------------------------------------------------------------- /DynamicIsland/components/HoverButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HoverButton.swift 3 | // DynamicIsland 4 | // 5 | // Created by Kraigo on 04.09.2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HoverButton: View { 11 | var icon: String 12 | var iconColor: Color = .white; 13 | var scale: Image.Scale = .medium 14 | var action: () -> Void 15 | var contentTransition: ContentTransition = .symbolEffect; 16 | 17 | @State private var isHovering = false 18 | 19 | var body: some View { 20 | let size = CGFloat(scale == .large ? 40 : 30) 21 | 22 | Button(action: action) { 23 | Rectangle() 24 | .fill(.clear) 25 | .contentShape(Rectangle()) 26 | .frame(width: size, height: size) 27 | .overlay { 28 | Capsule() 29 | .fill(isHovering ? Color.gray.opacity(0.2) : .clear) 30 | .frame(width: size, height: size) 31 | .overlay { 32 | Image(systemName: icon) 33 | .foregroundColor(iconColor) 34 | .contentTransition(contentTransition) 35 | .font(scale == .large ? .largeTitle : .body) 36 | } 37 | } 38 | } 39 | .buttonStyle(PlainButtonStyle()) 40 | .onHover { hovering in 41 | withAnimation(.smooth(duration: 0.3)) { 42 | isHovering = hovering 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DynamicIsland/components/Live activities/DownloadView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DownloadView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 17/08/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | enum Browser { 12 | case safari 13 | case chrome 14 | } 15 | 16 | struct DownloadFile { 17 | var name: String 18 | var size: Int 19 | var formattedSize: String 20 | var browser: Browser 21 | } 22 | 23 | class DownloadWatcher: ObservableObject { 24 | @Published var downloadFiles: [DownloadFile] = [] 25 | } 26 | 27 | struct DownloadArea: View { 28 | @EnvironmentObject var watcher: DownloadWatcher 29 | 30 | var body: some View { 31 | HStack(alignment: .center) { 32 | HStack { 33 | if watcher.downloadFiles.first!.browser == .safari { 34 | AppIcon(for: "com.apple.safari") 35 | } else { 36 | Image(.chrome).resizable().scaledToFit().frame(width: 30, height: 30) 37 | } 38 | VStack(alignment: .leading) { 39 | Text("Download") 40 | Text("In progress").font(.system(.footnote)).foregroundStyle(.gray) 41 | } 42 | } 43 | Spacer() 44 | HStack(spacing: 12) { 45 | VStack(alignment: .trailing) { 46 | Text(watcher.downloadFiles.first!.formattedSize) 47 | Text(watcher.downloadFiles.first!.name).font(.caption2).foregroundStyle(.gray) 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DynamicIsland/components/Live activities/LiveActivityModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiveActivityModifier.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 12/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum ActivityType { 11 | case mediaPlayback 12 | case charging 13 | case download 14 | } 15 | 16 | struct LiveActivityModifier: ViewModifier { 17 | let `for`: ActivityType 18 | let leftContent: () -> Left 19 | let rightContent: () -> Right 20 | 21 | func body(content: Content) -> some View { 22 | content 23 | .overlay( 24 | HStack { 25 | leftContent() 26 | Spacer() 27 | //.frame(minWidth: vm.closedNotchSize.width) 28 | rightContent() 29 | } 30 | .padding() 31 | ) 32 | } 33 | } 34 | 35 | extension View { 36 | func liveActivity( 37 | for activityId: ActivityType, 38 | @ViewBuilder left: @escaping () -> Left, 39 | @ViewBuilder right: @escaping () -> Right 40 | ) -> some View { 41 | self.modifier(LiveActivityModifier(for: activityId, leftContent: left, rightContent: right)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DynamicIsland/components/Live activities/MarqueeTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarqueeTextView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 08/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SizePreferenceKey: PreferenceKey { 11 | static var defaultValue: CGSize = .zero 12 | static func reduce(value: inout CGSize, nextValue: () -> CGSize) { 13 | value = nextValue() 14 | } 15 | } 16 | 17 | struct MeasureSizeModifier: ViewModifier { 18 | func body(content: Content) -> some View { 19 | content.background(GeometryReader { geometry in 20 | Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size) 21 | }) 22 | } 23 | } 24 | 25 | struct MarqueeText: View { 26 | @Binding var text: String 27 | let font: Font 28 | let nsFont: NSFont.TextStyle 29 | let textColor: Color 30 | let backgroundColor: Color 31 | let minDuration: Double 32 | let frameWidth: CGFloat 33 | 34 | @State private var animate = false 35 | @State private var textSize: CGSize = .zero 36 | @State private var offset: CGFloat = 0 37 | 38 | init(_ text: Binding, font: Font = .body, nsFont: NSFont.TextStyle = .body, textColor: Color = .primary, backgroundColor: Color = .clear, minDuration: Double = 3.0, frameWidth: CGFloat = 200) { 39 | _text = text 40 | self.font = font 41 | self.nsFont = nsFont 42 | self.textColor = textColor 43 | self.backgroundColor = backgroundColor 44 | self.minDuration = minDuration 45 | self.frameWidth = frameWidth 46 | } 47 | 48 | private var needsScrolling: Bool { 49 | textSize.width > frameWidth 50 | } 51 | 52 | var body: some View { 53 | GeometryReader { geometry in 54 | ZStack(alignment: .leading) { 55 | HStack(spacing: 20) { 56 | Text(text) 57 | Text(text) 58 | .opacity(needsScrolling ? 1 : 0) 59 | } 60 | .id(text) 61 | .font(font) 62 | .foregroundColor(textColor) 63 | .fixedSize(horizontal: true, vertical: false) 64 | .offset(x: self.animate ? offset : 0) 65 | .animation( 66 | self.animate ? 67 | .linear(duration: Double(textSize.width / 30)) 68 | .delay(minDuration) 69 | .repeatForever(autoreverses: false) : .none, 70 | value: self.animate 71 | ) 72 | .background(backgroundColor) 73 | .modifier(MeasureSizeModifier()) 74 | .onPreferenceChange(SizePreferenceKey.self) { size in 75 | self.textSize = CGSize(width: size.width / 2, height: NSFont.preferredFont(forTextStyle: nsFont).pointSize) 76 | self.animate = false 77 | self.offset = 0 78 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.02){ 79 | if needsScrolling { 80 | self.animate = true 81 | self.offset = -(textSize.width + 20) 82 | } 83 | } 84 | } 85 | .onChange(of: text) { _, _ in 86 | // Reset animation when text changes 87 | self.animate = false 88 | self.offset = 0 89 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.02){ 90 | if needsScrolling { 91 | self.animate = true 92 | self.offset = -(textSize.width + 20) 93 | } 94 | } 95 | } 96 | } 97 | .frame(width: frameWidth, alignment: .leading) 98 | .clipped() 99 | } 100 | .frame(height: textSize.height * 1.3) 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /DynamicIsland/components/MinimalisticMusicView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MinimalisticMusicView.swift 3 | // DynamicIsland 4 | // 5 | // Created for minimalistic UI mode 6 | // A clean, focused music player for closed notch state 7 | // 8 | 9 | import SwiftUI 10 | import Defaults 11 | 12 | struct MinimalisticMusicView: View { 13 | @EnvironmentObject var vm: DynamicIslandViewModel 14 | @ObservedObject var musicManager = MusicManager.shared 15 | @State private var isHovering: Bool = false 16 | 17 | var body: some View { 18 | HStack(spacing: 0) { 19 | // Left: Album Art 20 | albumArtView 21 | 22 | // Middle: Song Title (scrolling if needed) 23 | Rectangle() 24 | .fill(.black) 25 | .overlay( 26 | HStack(alignment: .center) { 27 | if !musicManager.songTitle.isEmpty { 28 | MarqueeText( 29 | .constant(musicManager.songTitle), 30 | textColor: Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor) : Color.gray, 31 | minDuration: 0.4, 32 | frameWidth: 100 33 | ) 34 | } 35 | } 36 | ) 37 | .frame(width: vm.closedNotchSize.width) 38 | 39 | // Right: Music Visualizer 40 | visualizerView 41 | } 42 | .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) 43 | .onHover { hovering in 44 | isHovering = hovering 45 | } 46 | } 47 | 48 | // MARK: - Album Art 49 | 50 | private var albumArtView: some View { 51 | HStack { 52 | Color.clear 53 | .aspectRatio(1, contentMode: .fit) 54 | .background( 55 | Image(nsImage: musicManager.albumArt) 56 | .resizable() 57 | .aspectRatio(contentMode: .fill) 58 | ) 59 | .clipped() 60 | .clipShape(RoundedRectangle(cornerRadius: 18)) // Dramatically increased corner radius for minimalistic mode 61 | .frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12)) 62 | } 63 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12))) 64 | } 65 | 66 | // MARK: - Visualizer 67 | 68 | private var visualizerView: some View { 69 | HStack { 70 | Rectangle() 71 | .fill(Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor).gradient : Color.gray.gradient) 72 | .frame(width: 50, alignment: .center) 73 | .mask { 74 | AudioSpectrumView(isPlaying: $musicManager.isPlaying) 75 | .frame(width: 16, height: 12) 76 | } 77 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), 78 | height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) 79 | } 80 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), 81 | height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DynamicIsland/components/Music/LottieAnimationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LottieAnimationView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 2024. 10. 29.. 6 | // 7 | 8 | import SwiftUI 9 | import Lottie 10 | import LottieUI 11 | import Defaults 12 | 13 | struct LottieAnimationView: View { 14 | let state1 = LUStateData(type: .loadedFrom(URL(string: "https://assets9.lottiefiles.com/packages/lf20_mniampqn.json")!), speed: 1.0, loopMode: .loop) 15 | @Default(.selectedVisualizer) var selectedVisualizer 16 | var body: some View { 17 | if selectedVisualizer == nil { 18 | LottieView(state: state1) 19 | } else { 20 | LottieView( 21 | state: LUStateData( 22 | type: .loadedFrom(selectedVisualizer!.url), 23 | speed: selectedVisualizer!.speed, 24 | loopMode: .loop 25 | ) 26 | ) 27 | } 28 | } 29 | } 30 | 31 | #Preview { 32 | LottieAnimationView() 33 | } 34 | -------------------------------------------------------------------------------- /DynamicIsland/components/Music/MusicVisualizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MusicVisualizer.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 02/08/24. 6 | // 7 | import AppKit 8 | import Cocoa 9 | import SwiftUI 10 | 11 | class AudioSpectrum: NSView { 12 | private var barLayers: [CAShapeLayer] = [] 13 | private var isPlaying: Bool = true 14 | private var animationTimer: Timer? 15 | 16 | override init(frame frameRect: NSRect) { 17 | super.init(frame: frameRect) 18 | wantsLayer = true 19 | setupBars() 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | super.init(coder: coder) 24 | wantsLayer = true 25 | setupBars() 26 | } 27 | 28 | private func setupBars() { 29 | let barWidth: CGFloat = 2 30 | let barCount = 4 31 | let spacing: CGFloat = barWidth 32 | let totalWidth = CGFloat(barCount) * (barWidth + spacing) 33 | let totalHeight: CGFloat = 14 34 | frame.size = CGSize(width: totalWidth, height: totalHeight) 35 | 36 | for i in 0 ..< barCount { 37 | let xPosition = CGFloat(i) * (barWidth + spacing) 38 | let barLayer = CAShapeLayer() 39 | barLayer.frame = CGRect(x: xPosition, y: 0, width: barWidth, height: totalHeight) 40 | barLayer.position = CGPoint(x: xPosition + barWidth / 2, y: totalHeight / 2) 41 | barLayer.fillColor = NSColor.white.cgColor 42 | 43 | let path = NSBezierPath(roundedRect: CGRect(x: 0, y: 0, width: barWidth, height: totalHeight), 44 | xRadius: barWidth / 2, 45 | yRadius: barWidth / 2) 46 | barLayer.path = path.cgPath 47 | 48 | barLayers.append(barLayer) 49 | layer?.addSublayer(barLayer) 50 | } 51 | } 52 | 53 | private func startAnimating() { 54 | guard animationTimer == nil else { return } 55 | animationTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] _ in 56 | self?.updateBars() 57 | } 58 | } 59 | 60 | private func stopAnimating() { 61 | animationTimer?.invalidate() 62 | animationTimer = nil 63 | resetBars() 64 | } 65 | 66 | private func updateBars() { 67 | for barLayer in barLayers { 68 | let animation = CABasicAnimation(keyPath: "transform.scale.y") 69 | animation.fromValue = barLayer.presentation()?.value(forKeyPath: "transform.scale.y") ?? 0.35 70 | animation.toValue = CGFloat.random(in: 0.35 ... 1.0) 71 | animation.duration = 0.3 72 | animation.autoreverses = true 73 | animation.fillMode = .forwards 74 | animation.isRemovedOnCompletion = false 75 | if #available(macOS 13.0, *) { 76 | animation.preferredFrameRateRange = CAFrameRateRange(minimum: 24, maximum: 24, preferred: 24) 77 | } 78 | barLayer.add(animation, forKey: "scaleY") 79 | } 80 | } 81 | 82 | private func resetBars() { 83 | for barLayer in barLayers { 84 | barLayer.removeAllAnimations() 85 | barLayer.transform = CATransform3DMakeScale(1, 0.35, 1) 86 | } 87 | } 88 | 89 | func setPlaying(_ playing: Bool) { 90 | isPlaying = playing 91 | if isPlaying { 92 | startAnimating() 93 | } else { 94 | stopAnimating() 95 | } 96 | } 97 | } 98 | 99 | struct AudioSpectrumView: NSViewRepresentable { 100 | @Binding var isPlaying: Bool 101 | 102 | func makeNSView(context: Context) -> AudioSpectrum { 103 | let spectrum = AudioSpectrum() 104 | spectrum.setPlaying(isPlaying) 105 | return spectrum 106 | } 107 | 108 | func updateNSView(_ nsView: AudioSpectrum, context: Context) { 109 | nsView.setPlaying(isPlaying) 110 | } 111 | } 112 | 113 | #Preview { 114 | AudioSpectrumView(isPlaying: .constant(true)) 115 | .frame(width: 16, height: 20) 116 | .padding() 117 | } 118 | -------------------------------------------------------------------------------- /DynamicIsland/components/Notch/DynamicIslandExtrasMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicIslandExtrasMenu.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DynamicIslandLargeButtons: View { 11 | var action: () -> Void 12 | var icon: Image 13 | var title: String 14 | var body: some View { 15 | Button ( 16 | action:action, 17 | label: { 18 | ZStack { 19 | RoundedRectangle(cornerRadius: 12.0).fill(.black).frame(width: 70, height: 70) 20 | VStack(spacing: 8) { 21 | icon.resizable() 22 | .aspectRatio(contentMode: .fit).frame(width:20) 23 | Text(title).font(.body) 24 | } 25 | } 26 | }).buttonStyle(PlainButtonStyle()).shadow(color: .black.opacity(0.5), radius: 10) 27 | } 28 | } 29 | 30 | struct DynamicIslandExtrasMenu : View { 31 | @ObservedObject var vm: DynamicIslandViewModel 32 | 33 | var body: some View { 34 | VStack{ 35 | HStack(spacing: 20) { 36 | hide 37 | settings 38 | close 39 | } 40 | } 41 | } 42 | 43 | var github: some View { 44 | DynamicIslandLargeButtons( 45 | action: { 46 | NSWorkspace.shared.open(productPage) 47 | }, 48 | icon: Image(.github), 49 | title: "Checkout" 50 | ) 51 | } 52 | 53 | var donate: some View { 54 | DynamicIslandLargeButtons( 55 | action: { 56 | NSWorkspace.shared.open(sponsorPage) 57 | }, 58 | icon: Image(systemName: "heart.fill"), 59 | title: "Love Us" 60 | ) 61 | } 62 | 63 | var settings: some View { 64 | Button(action: { 65 | SettingsWindowController.shared.showWindow() 66 | }) { 67 | ZStack { 68 | RoundedRectangle(cornerRadius: 12.0).fill(.black).frame(width: 70, height: 70) 69 | VStack(spacing: 8) { 70 | Image(systemName: "gear").resizable() 71 | .aspectRatio(contentMode: .fit).frame(width:20) 72 | Text("Settings").font(.body) 73 | } 74 | } 75 | } 76 | .buttonStyle(PlainButtonStyle()).shadow(color: .black.opacity(0.5), radius: 10) 77 | } 78 | 79 | var hide: some View { 80 | DynamicIslandLargeButtons( 81 | action: { 82 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { 83 | //vm.openMusic() 84 | } 85 | }, 86 | icon: Image(systemName: "arrow.down.forward.and.arrow.up.backward"), 87 | title: "Hide" 88 | ) 89 | } 90 | 91 | var close: some View { 92 | DynamicIslandLargeButtons( 93 | action: { 94 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { 95 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { 96 | NSApp.terminate(nil) 97 | } 98 | } 99 | }, 100 | icon: Image(systemName: "xmark"), 101 | title: "Exit" 102 | ) 103 | } 104 | } 105 | 106 | 107 | #Preview { 108 | DynamicIslandExtrasMenu(vm: DynamicIslandViewModel()) 109 | } 110 | -------------------------------------------------------------------------------- /DynamicIsland/components/Notch/DynamicIslandWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicIslandWindow.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 06/08/24. 6 | // 7 | 8 | import Cocoa 9 | 10 | class DynamicIslandWindow: NSPanel { 11 | override init( 12 | contentRect: NSRect, 13 | styleMask: NSWindow.StyleMask, 14 | backing: NSWindow.BackingStoreType, 15 | defer flag: Bool 16 | ) { 17 | super.init( 18 | contentRect: contentRect, 19 | styleMask: styleMask, 20 | backing: backing, 21 | defer: flag 22 | ) 23 | 24 | isFloatingPanel = true 25 | isOpaque = false 26 | titleVisibility = .hidden 27 | titlebarAppearsTransparent = true 28 | backgroundColor = .clear 29 | isMovable = false 30 | 31 | collectionBehavior = [ 32 | .fullScreenAuxiliary, 33 | .stationary, 34 | .canJoinAllSpaces, 35 | .ignoresCycle, 36 | ] 37 | 38 | isReleasedWhenClosed = false 39 | level = .mainMenu + 3 40 | hasShadow = false 41 | } 42 | 43 | override var canBecomeKey: Bool { 44 | true 45 | } 46 | 47 | override var canBecomeMain: Bool { 48 | true 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DynamicIsland/components/Notch/NotchShape.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotchShape.swift 3 | // DynamicIsland 4 | // 5 | // Created by Kai Azim on 2023-08-24. 6 | // Original source: https://github.com/MrKai77/DynamicNotchKit 7 | // Modified by Alexander on 2025-05-18. 8 | 9 | import SwiftUI 10 | 11 | struct NotchShape: Shape { 12 | private var topCornerRadius: CGFloat 13 | private var bottomCornerRadius: CGFloat 14 | 15 | init( 16 | topCornerRadius: CGFloat? = nil, 17 | bottomCornerRadius: CGFloat? = nil 18 | ) { 19 | self.topCornerRadius = topCornerRadius ?? 6 20 | self.bottomCornerRadius = bottomCornerRadius ?? 14 21 | } 22 | 23 | var animatableData: AnimatablePair { 24 | get { 25 | .init( 26 | topCornerRadius, 27 | bottomCornerRadius 28 | ) 29 | } 30 | set { 31 | topCornerRadius = newValue.first 32 | bottomCornerRadius = newValue.second 33 | } 34 | } 35 | 36 | func path(in rect: CGRect) -> Path { 37 | var path = Path() 38 | 39 | path.move( 40 | to: CGPoint( 41 | x: rect.minX, 42 | y: rect.minY 43 | ) 44 | ) 45 | 46 | path.addQuadCurve( 47 | to: CGPoint( 48 | x: rect.minX + topCornerRadius, 49 | y: rect.minY + topCornerRadius 50 | ), 51 | control: CGPoint( 52 | x: rect.minX + topCornerRadius, 53 | y: rect.minY 54 | ) 55 | ) 56 | 57 | path.addLine( 58 | to: CGPoint( 59 | x: rect.minX + topCornerRadius, 60 | y: rect.maxY - bottomCornerRadius 61 | ) 62 | ) 63 | 64 | path.addQuadCurve( 65 | to: CGPoint( 66 | x: rect.minX + topCornerRadius + bottomCornerRadius, 67 | y: rect.maxY 68 | ), 69 | control: CGPoint( 70 | x: rect.minX + topCornerRadius, 71 | y: rect.maxY 72 | ) 73 | ) 74 | 75 | path.addLine( 76 | to: CGPoint( 77 | x: rect.maxX - topCornerRadius - bottomCornerRadius, 78 | y: rect.maxY 79 | ) 80 | ) 81 | 82 | path.addQuadCurve( 83 | to: CGPoint( 84 | x: rect.maxX - topCornerRadius, 85 | y: rect.maxY - bottomCornerRadius 86 | ), 87 | control: CGPoint( 88 | x: rect.maxX - topCornerRadius, 89 | y: rect.maxY 90 | ) 91 | ) 92 | 93 | path.addLine( 94 | to: CGPoint( 95 | x: rect.maxX - topCornerRadius, 96 | y: rect.minY + topCornerRadius 97 | ) 98 | ) 99 | 100 | path.addQuadCurve( 101 | to: CGPoint( 102 | x: rect.maxX, 103 | y: rect.minY 104 | ), 105 | control: CGPoint( 106 | x: rect.maxX - topCornerRadius, 107 | y: rect.minY 108 | ) 109 | ) 110 | 111 | path.addLine( 112 | to: CGPoint( 113 | x: rect.minX, 114 | y: rect.minY 115 | ) 116 | ) 117 | 118 | return path 119 | } 120 | } 121 | 122 | #Preview { 123 | NotchShape(topCornerRadius: 6, bottomCornerRadius: 14) 124 | .frame(width: 200, height: 32) 125 | .padding(10) 126 | } 127 | -------------------------------------------------------------------------------- /DynamicIsland/components/Notch/NotchShelfView.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct NotchShelfView: View { 5 | @EnvironmentObject var vm: DynamicIslandViewModel 6 | @ObservedObject var tvm = TrayDrop.shared 7 | 8 | var body: some View { 9 | HStack { 10 | AirDropView() 11 | panel 12 | .onDrop(of: [.data], isTargeted: $vm.dropZoneTargeting) { providers in 13 | vm.dropEvent = true 14 | DispatchQueue.global().async { 15 | tvm.load(providers) 16 | } 17 | return true 18 | } 19 | } 20 | } 21 | 22 | var panel: some View { 23 | RoundedRectangle(cornerRadius: 16) 24 | .strokeBorder(style: StrokeStyle(lineWidth: 4, dash: [10])) 25 | .foregroundStyle(.white.opacity(0.1)) 26 | .overlay { 27 | content 28 | .padding() 29 | } 30 | .animation(vm.animation, value: tvm.items) 31 | .animation(vm.animation, value: tvm.isLoading) 32 | } 33 | 34 | var content: some View { 35 | Group { 36 | if tvm.isEmpty { 37 | VStack(spacing: 10) { 38 | Image(systemName: "tray.and.arrow.down") 39 | .symbolVariant(.fill) 40 | .symbolRenderingMode(.hierarchical) 41 | .foregroundStyle(.white, .gray) 42 | .imageScale(.large) 43 | 44 | Text("Drop files here") 45 | .foregroundStyle(.gray) 46 | .font(.system(.title3, design: .rounded)) 47 | .fontWeight(.medium) 48 | } 49 | } else { 50 | ScrollView(.horizontal) { 51 | HStack(spacing: spacing) { 52 | ForEach(tvm.items) { item in 53 | DropItemView(item: item) 54 | } 55 | } 56 | .padding(spacing) 57 | } 58 | .padding(-spacing) 59 | .scrollIndicators(.never) 60 | } 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /DynamicIsland/components/Onboarding/ActivationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingSettings.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 2024. 09. 26.. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ActivationWindow: View { 11 | @State private var email: String = "" 12 | @State private var key: String = "" 13 | var body: some View { 14 | VStack { 15 | Image("logo") 16 | .resizable() 17 | .aspectRatio(contentMode: .fit) 18 | .frame(height: 80) 19 | .padding(.top, 30) 20 | .padding(.bottom, 10) 21 | Text("Activate your license") 22 | .font(.largeTitle.bold()) 23 | .fontDesign(.rounded) 24 | Text("Transform your notch truly yours") 25 | .foregroundStyle(.secondary) 26 | .font(.title2) 27 | .padding(.bottom, 20) 28 | Group { 29 | TextField("Email address", text: $email) 30 | .padding(.horizontal, 8) 31 | .padding(.vertical, 4) 32 | .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 8)) 33 | TextField("License key", text: $key) 34 | .padding(.horizontal, 8) 35 | .padding(.vertical, 4) 36 | .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 8)) 37 | } 38 | .textFieldStyle(PlainTextFieldStyle()) 39 | .scrollContentBackground(.hidden) 40 | .toggleStyle(.switch) 41 | Spacer() 42 | VStack(alignment: .center, spacing: 14) { 43 | HStack(alignment: .center) { 44 | HStack { 45 | Button {} label: { 46 | Text("Cancel") 47 | .padding(.horizontal, 18) 48 | } 49 | .buttonStyle(AccessoryBarButtonStyle()) 50 | } 51 | .frame(maxWidth: .infinity, alignment: .leading) 52 | Image("dynamicisland") 53 | .resizable() 54 | .aspectRatio(contentMode: .fit) 55 | .frame(height: 18) 56 | .offset(y: 4) 57 | .blendMode(.overlay) 58 | HStack { 59 | Button {} label: { 60 | Text("Activate") 61 | .padding(.horizontal, 18) 62 | } 63 | .buttonStyle(BorderedProminentButtonStyle()) 64 | } 65 | .frame(maxWidth: .infinity, alignment: .trailing) 66 | 67 | } 68 | .controlSize(.extraLarge) 69 | } 70 | } 71 | .padding() 72 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) 73 | .ignoresSafeArea() 74 | .background { 75 | VisualEffectView(material: .hudWindow, blendingMode: .behindWindow) 76 | .ignoresSafeArea() 77 | } 78 | .frame(width: 350, height: 350) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /DynamicIsland/components/Onboarding/OnboardingFinishView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingFinishView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-06-23. 6 | // 7 | 8 | 9 | import SwiftUI 10 | 11 | struct OnboardingFinishView: View { 12 | let onFinish: () -> Void 13 | let onOpenSettings: () -> Void 14 | 15 | var body: some View { 16 | VStack(spacing: 20) { 17 | Spacer() 18 | 19 | Image(systemName: "sparkles") 20 | .font(.system(size: 60)) 21 | .foregroundColor(.accentColor) 22 | .padding() 23 | 24 | Text("You're All Set!") 25 | .font(.largeTitle) 26 | .fontWeight(.bold) 27 | 28 | Text("You can now enjoy the app. If you want to tweak things further, you can always visit the settings.") 29 | .font(.body) 30 | .foregroundColor(.secondary) 31 | .multilineTextAlignment(.center) 32 | .padding(.horizontal, 40) 33 | 34 | Spacer() 35 | Spacer() 36 | 37 | VStack(spacing: 12) { 38 | Button(action: onOpenSettings) { 39 | Label("Customize in Settings", systemImage: "gear") 40 | .controlSize(.large) 41 | } 42 | .controlSize(.large) 43 | 44 | Button("Finish", action: onFinish) 45 | .buttonStyle(.borderedProminent) 46 | .controlSize(.large) 47 | .keyboardShortcut(.defaultAction) 48 | 49 | // Privacy Policy Link 50 | Button(action: { 51 | if let url = URL(string: "https://ebullioscopic.github.io/DynamicIsland/privacy-policy") { 52 | NSWorkspace.shared.open(url) 53 | } 54 | }) { 55 | Text("Privacy Policy") 56 | .font(.caption) 57 | .foregroundColor(.secondary) 58 | } 59 | .buttonStyle(.plain) 60 | .padding(.top, 4) 61 | } 62 | .padding(24) 63 | } 64 | .frame(maxWidth: .infinity, maxHeight: .infinity) 65 | .background( 66 | VisualEffectView(material: .underWindowBackground, blendingMode: .behindWindow) 67 | .ignoresSafeArea() 68 | ) 69 | } 70 | } 71 | 72 | #Preview { 73 | OnboardingFinishView(onFinish: { }, onOpenSettings: { }) 74 | } 75 | -------------------------------------------------------------------------------- /DynamicIsland/components/Onboarding/PermissionsRequestView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PermissionsRequestView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-06-23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PermissionRequestView: View { 11 | let icon: Image 12 | let title: String 13 | let description: String 14 | let privacyNote: String? 15 | let onAllow: () -> Void 16 | let onSkip: () -> Void 17 | 18 | var body: some View { 19 | VStack(spacing: 28) { 20 | icon 21 | .resizable() 22 | .scaledToFit() 23 | .frame(width: 70, height: 56) 24 | .foregroundColor(.accentColor) 25 | .padding(.top, 32) 26 | 27 | Text(title) 28 | .font(.title) 29 | .fontWeight(.semibold) 30 | 31 | Text(description) 32 | .multilineTextAlignment(.center) 33 | .padding(.horizontal) 34 | 35 | if let privacyNote = privacyNote { 36 | HStack(spacing: 8) { 37 | Image(systemName: "lock.shield") 38 | .foregroundColor(.secondary) 39 | Text(privacyNote) 40 | .font(.subheadline) 41 | .foregroundColor(.secondary) 42 | .multilineTextAlignment(.leading) 43 | } 44 | .padding(.bottom, 8) 45 | .padding(.horizontal) 46 | } 47 | 48 | HStack { 49 | Button("Not Now") { onSkip() } 50 | .buttonStyle(.bordered) 51 | Button("Allow Access") { onAllow() } 52 | .buttonStyle(.borderedProminent) 53 | } 54 | .padding(.top, 10) 55 | } 56 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) 57 | .background( 58 | VisualEffectView(material: .underWindowBackground, blendingMode: .behindWindow) 59 | .ignoresSafeArea() 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DynamicIsland/components/Onboarding/SparkleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SparkleView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 2024. 09. 26.. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | 11 | class SparkleNSView: NSView { 12 | private var emitterLayer: CAEmitterLayer? 13 | 14 | override init(frame frameRect: NSRect) { 15 | super.init(frame: frameRect) 16 | self.wantsLayer = true 17 | setupEmitterLayer() 18 | } 19 | 20 | required init?(coder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | private func setupEmitterLayer() { 25 | let emitterLayer = CAEmitterLayer() 26 | emitterLayer.emitterShape = .rectangle 27 | emitterLayer.emitterMode = .surface 28 | emitterLayer.renderMode = .oldestFirst 29 | 30 | let cell = CAEmitterCell() 31 | cell.contents = NSImage(named: "sparkle")?.cgImage(forProposedRect: nil, context: nil, hints: nil) 32 | cell.birthRate = 50 33 | cell.lifetime = 5 34 | cell.velocity = 10 35 | cell.velocityRange = 5 36 | cell.emissionRange = .pi * 2 37 | cell.scale = 0.2 38 | cell.scaleRange = 0.1 39 | cell.alphaSpeed = -0.5 40 | cell.yAcceleration = 10 // Add a slight downward motion 41 | 42 | emitterLayer.emitterCells = [cell] 43 | 44 | self.layer?.addSublayer(emitterLayer) 45 | self.emitterLayer = emitterLayer 46 | 47 | updateEmitterForCurrentBounds() 48 | } 49 | 50 | private func updateEmitterForCurrentBounds() { 51 | guard let emitterLayer = self.emitterLayer else { return } 52 | 53 | emitterLayer.frame = self.bounds 54 | emitterLayer.emitterSize = self.bounds.size 55 | emitterLayer.emitterPosition = CGPoint(x: bounds.width / 2, y: bounds.height / 2) 56 | 57 | // Adjust birth rate based on view size 58 | let area = bounds.width * bounds.height 59 | let baseBirthRate: Float = 50 60 | let adjustedBirthRate = 20 // Assuming 200x200 as base size 61 | emitterLayer.emitterCells?.first?.birthRate = Float(adjustedBirthRate) 62 | } 63 | 64 | override func setFrameSize(_ newSize: NSSize) { 65 | super.setFrameSize(newSize) 66 | updateEmitterForCurrentBounds() 67 | } 68 | } 69 | 70 | struct SparkleView: NSViewRepresentable { 71 | func makeNSView(context: Context) -> SparkleNSView { 72 | return SparkleNSView() 73 | } 74 | 75 | func updateNSView(_ nsView: SparkleNSView, context: Context) {} 76 | } 77 | -------------------------------------------------------------------------------- /DynamicIsland/components/Onboarding/WelcomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WelcomeView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 2024. 09. 26.. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftUIIntrospect 10 | 11 | struct WelcomeView: View { 12 | var onGetStarted: (() -> Void)? = nil 13 | var body: some View { 14 | ZStack(alignment: .top) { 15 | ZStack { 16 | Image("spotlight") 17 | .resizable() 18 | .aspectRatio(contentMode: .fit) 19 | .padding(.bottom) 20 | .blur(radius: 3) 21 | .offset(y: -5) 22 | .background(SparkleView().opacity(0.6)) 23 | VStack(spacing: 8) { 24 | Image("logo2") 25 | .resizable() 26 | .aspectRatio(contentMode: .fit) 27 | .frame(width: 100, height: 100) 28 | .padding(.bottom, 8) 29 | Text("Dynamic Island") 30 | .font(.system(.largeTitle, design: .default)) 31 | .fontWeight(.semibold) 32 | Text("Welcome") 33 | .font(.title) 34 | .foregroundStyle(.secondary) 35 | .padding(.bottom, 30) 36 | if false { 37 | Text("PRO") 38 | .font(.system(size: 18, design: .rounded)) 39 | .fontWeight(.bold) 40 | .foregroundStyle(.white) 41 | .padding(.horizontal, 12) 42 | .padding(.vertical, 3) 43 | .background( 44 | Capsule() 45 | .fill(LinearGradient(colors: [.white.opacity(0.7), .white.opacity(0.3)], startPoint: .topLeading, endPoint: .bottomTrailing)) 46 | .strokeBorder(LinearGradient(stops: [.init(color: .white.opacity(0.7), location: 0.3), .init(color: .clear, location: 0.6)], startPoint: .topLeading, endPoint: .bottomTrailing)) 47 | .blendMode(.overlay) 48 | ) 49 | .padding(.bottom, 30) 50 | } 51 | 52 | 53 | Button { 54 | onGetStarted?() 55 | } label: { 56 | Text("Get started") 57 | .padding(.horizontal, 20) 58 | .padding(.vertical, 6) 59 | } 60 | .buttonStyle(BorderedProminentButtonStyle()) 61 | 62 | // Privacy Policy Link 63 | Button(action: { 64 | if let url = URL(string: "https://ebullioscopic.github.io/DynamicIsland/privacy-policy") { 65 | NSWorkspace.shared.open(url) 66 | } 67 | }) { 68 | Text("Privacy Policy") 69 | .font(.caption) 70 | .foregroundColor(.secondary) 71 | } 72 | .buttonStyle(.plain) 73 | .padding(.top, 8) 74 | } 75 | .padding(.top) 76 | } 77 | 78 | Image("ebullioscopic") 79 | .resizable() 80 | .aspectRatio(contentMode: .fit) 81 | .frame(height: 22) 82 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) 83 | .padding() 84 | .padding(.bottom, 36) 85 | .blendMode(.overlay) 86 | } 87 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) 88 | .ignoresSafeArea() 89 | .background { 90 | VisualEffectView(material: .hudWindow, blendingMode: .behindWindow) 91 | .ignoresSafeArea() 92 | } 93 | } 94 | } 95 | 96 | #Preview { 97 | WelcomeView() 98 | } 99 | -------------------------------------------------------------------------------- /DynamicIsland/components/ProgressIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgressIndicator.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 11/08/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct CircularProgressView: View { 12 | let progress: Double 13 | let color: Color 14 | 15 | var body: some View { 16 | ZStack { 17 | Circle() 18 | .stroke( 19 | Color.white.opacity(0.2), 20 | lineWidth: 6 21 | ) 22 | Circle() 23 | .trim(from: 0, to: progress) 24 | .stroke( 25 | color, 26 | // 1 27 | style: StrokeStyle( 28 | lineWidth: 6, 29 | lineCap: .round 30 | ) 31 | ) 32 | .rotationEffect(.degrees(-90)) 33 | } 34 | } 35 | } 36 | 37 | enum ProgressIndicatorType { 38 | case circle 39 | case text 40 | } 41 | 42 | 43 | // based on type .circle or .text 44 | struct ProgressIndicator: View { 45 | var type: ProgressIndicatorType 46 | var progress: Double 47 | var color: Color 48 | 49 | var body: some View { 50 | switch type { 51 | case .circle: 52 | CircularProgressView(progress: progress, color: color).frame( 53 | width: 20, height: 20) 54 | case .text: 55 | Text("\(Int(progress * 100))%") 56 | } 57 | } 58 | } 59 | 60 | #Preview { 61 | ProgressIndicator(type: .circle, progress: 0.8, color: Color.blue).padding() 62 | .frame(width: 200, height: 200) 63 | } 64 | -------------------------------------------------------------------------------- /DynamicIsland/components/Recording/RecordingLiveActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecordingLiveActivity.swift 3 | // DynamicIsland 4 | // 5 | // Created for screen recording live activity 6 | // 7 | 8 | import SwiftUI 9 | import Defaults 10 | 11 | struct RecordingLiveActivity: View { 12 | @EnvironmentObject var vm: DynamicIslandViewModel 13 | @ObservedObject var recordingManager = ScreenRecordingManager.shared 14 | @State private var isHovering: Bool = false 15 | @State private var gestureProgress: CGFloat = 0 16 | @State private var isExpanded: Bool = false 17 | 18 | var body: some View { 19 | HStack(spacing: 0) { 20 | // Left - Red circle with animation 21 | Color.clear 22 | .background { 23 | if isExpanded { 24 | HStack { 25 | ZStack { 26 | RoundedRectangle(cornerRadius: 6) 27 | .fill(Color.red.opacity(0.15)) 28 | 29 | Circle() 30 | .fill(Color.red) 31 | .frame(width: 10, height: 10) 32 | .modifier(PulsingModifier()) 33 | } 34 | .frame(width: vm.effectiveClosedNotchHeight - 12, height: vm.effectiveClosedNotchHeight - 12) 35 | } 36 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) 37 | } 38 | } 39 | .frame(width: isExpanded ? max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2) : 0, height: vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)) 40 | 41 | // Center - Black fill 42 | Rectangle() 43 | .fill(.black) 44 | .frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0)) 45 | 46 | // Right - Empty for symmetry with animation 47 | Color.clear 48 | .frame(width: isExpanded ? max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2) : 0, height: vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)) 49 | } 50 | .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0)) 51 | .onAppear { 52 | withAnimation(.smooth(duration: 0.4)) { 53 | isExpanded = true 54 | } 55 | } 56 | .onChange(of: recordingManager.isRecording) { _, newValue in 57 | if !newValue { 58 | withAnimation(.smooth(duration: 0.4)) { 59 | isExpanded = false 60 | } 61 | } else { 62 | withAnimation(.smooth(duration: 0.4)) { 63 | isExpanded = true 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | // Pulsing animation modifier for recording indicator 71 | struct PulsingModifier: ViewModifier { 72 | @State private var isPulsing = false 73 | 74 | func body(content: Content) -> some View { 75 | content 76 | .scaleEffect(isPulsing ? 1.2 : 1.0) 77 | .opacity(isPulsing ? 0.7 : 1.0) 78 | .onAppear { 79 | withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) { 80 | isPulsing = true 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DynamicIsland/components/Settings/EditPanelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditPanelView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 12/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct EditPanelView: View { 11 | @State var wallpaperPath: URL? 12 | var body: some View { 13 | VStack { 14 | HStack { 15 | Text("Edit layout") 16 | .font(.system(.largeTitle, design: .rounded)) 17 | .foregroundColor(.white.opacity(0.5)) 18 | Spacer() 19 | Button { 20 | exit(0) 21 | } label: { 22 | Label("Close", systemImage: "xmark") 23 | } 24 | .controlSize(.extraLarge) 25 | .buttonStyle(AccessoryBarButtonStyle()) 26 | } 27 | .padding() 28 | } 29 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) 30 | } 31 | } 32 | 33 | #Preview { 34 | EditPanelView() 35 | } 36 | 37 | struct VisualEffectView: NSViewRepresentable { 38 | let material: NSVisualEffectView.Material 39 | let blendingMode: NSVisualEffectView.BlendingMode 40 | 41 | func makeNSView(context _: Context) -> NSVisualEffectView { 42 | let visualEffectView = NSVisualEffectView() 43 | visualEffectView.material = material 44 | visualEffectView.blendingMode = blendingMode 45 | visualEffectView.state = NSVisualEffectView.State.active 46 | visualEffectView.isEmphasized = true 47 | return visualEffectView 48 | } 49 | 50 | func updateNSView(_ visualEffectView: NSVisualEffectView, context _: Context) { 51 | visualEffectView.material = material 52 | visualEffectView.blendingMode = blendingMode 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DynamicIsland/components/Settings/ListItemPopover.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListItemPopover.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 15/09/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ListItemPopover: View { 11 | let content: () -> Content 12 | 13 | @State private var isPresented: Bool = false 14 | var body: some View { 15 | Button { 16 | isPresented.toggle() 17 | } label: { 18 | Image(systemName: "info.circle") 19 | .foregroundStyle(.secondary) 20 | } 21 | .controlSize(.regular) 22 | .popover(isPresented: $isPresented, attachmentAnchor: .rect(.bounds), arrowEdge: .trailing, content: { 23 | content() 24 | .padding() 25 | }) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /DynamicIsland/components/Settings/SoftwareUpdater.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareUpdater.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 09/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | import Sparkle 10 | 11 | final class CheckForUpdatesViewModel: ObservableObject { 12 | @Published var canCheckForUpdates = false 13 | 14 | init(updater: SPUUpdater) { 15 | updater.publisher(for: \.canCheckForUpdates) 16 | .assign(to: &$canCheckForUpdates) 17 | } 18 | } 19 | 20 | struct CheckForUpdatesView: View { 21 | @ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel 22 | private let updater: SPUUpdater 23 | 24 | init(updater: SPUUpdater) { 25 | self.updater = updater 26 | 27 | // Create our view model for our CheckForUpdatesView 28 | self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater) 29 | } 30 | 31 | var body: some View { 32 | Button("Check for Updates…", action: updater.checkForUpdates) 33 | .disabled(!checkForUpdatesViewModel.canCheckForUpdates) 34 | } 35 | } 36 | 37 | struct UpdaterSettingsView: View { 38 | private let updater: SPUUpdater 39 | 40 | @State private var automaticallyChecksForUpdates: Bool 41 | @State private var automaticallyDownloadsUpdates: Bool 42 | 43 | init(updater: SPUUpdater) { 44 | self.updater = updater 45 | self.automaticallyChecksForUpdates = updater.automaticallyChecksForUpdates 46 | self.automaticallyDownloadsUpdates = updater.automaticallyDownloadsUpdates 47 | } 48 | 49 | var body: some View { 50 | Section { 51 | Toggle("Automatically check for updates", isOn: $automaticallyChecksForUpdates) 52 | .onChange(of: automaticallyChecksForUpdates) { _, newValue in 53 | updater.automaticallyChecksForUpdates = newValue 54 | } 55 | 56 | Toggle("Automatically download updates", isOn: $automaticallyDownloadsUpdates) 57 | .disabled(!automaticallyChecksForUpdates) 58 | .onChange(of: automaticallyDownloadsUpdates) { _, newValue in 59 | updater.automaticallyDownloadsUpdates = newValue 60 | } 61 | } header: { 62 | HStack { 63 | Text("Software updates") 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/AirDrop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirDrop.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/7. 6 | // 7 | 8 | import Cocoa 9 | 10 | class AirDrop: NSObject, NSSharingServiceDelegate { 11 | let files: [URL] 12 | 13 | init(files: [URL]) { 14 | self.files = files 15 | super.init() 16 | } 17 | 18 | func begin() { 19 | do { 20 | try sendEx(files) 21 | } catch { 22 | NSAlert.popError(error) 23 | } 24 | } 25 | 26 | private func sendEx(_ files: [URL]) throws { 27 | guard let service = NSSharingService(named: .sendViaAirDrop) else { 28 | throw NSError(domain: "AirDrop", code: 1, userInfo: [ 29 | NSLocalizedDescriptionKey: NSLocalizedString("AirDrop service not available", comment: ""), 30 | ]) 31 | } 32 | guard service.canPerform(withItems: files) else { 33 | throw NSError(domain: "AirDrop", code: 2, userInfo: [ 34 | NSLocalizedDescriptionKey: NSLocalizedString("AirDrop service not available", comment: ""), 35 | ]) 36 | } 37 | service.delegate = self 38 | service.perform(withItems: files) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/AirDropView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AirDrop+View.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import SwiftUI 9 | import UniformTypeIdentifiers 10 | 11 | struct AirDropView: View { 12 | @EnvironmentObject var vm: DynamicIslandViewModel 13 | 14 | @State var trigger: UUID = .init() 15 | @State var targeting = false 16 | 17 | var body: some View { 18 | dropArea 19 | .onDrop(of: [.data], isTargeted: $vm.dropZoneTargeting) { providers in 20 | trigger = .init() 21 | vm.dropEvent = true 22 | DispatchQueue.global().async { beginDrop(providers) } 23 | return true 24 | } 25 | } 26 | 27 | var dropArea: some View { 28 | Rectangle() 29 | .fill(.white.opacity(0.1)) 30 | .opacity(0.5) 31 | .clipShape(RoundedRectangle(cornerRadius: 10)) 32 | .overlay { dropLabel } 33 | .aspectRatio(1, contentMode: .fit) 34 | .contentShape(Rectangle()) 35 | } 36 | 37 | var dropLabel: some View { 38 | VStack(spacing: 8) { 39 | Image(systemName: "airplayaudio") 40 | Text("AirDrop") 41 | } 42 | .foregroundStyle(.gray) 43 | .font(.system(.headline, design: .rounded)) 44 | .contentShape(Rectangle()) 45 | .onTapGesture { 46 | trigger = .init() 47 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 48 | let picker = NSOpenPanel() 49 | picker.allowsMultipleSelection = true 50 | picker.canChooseDirectories = true 51 | picker.canChooseFiles = true 52 | picker.begin { response in 53 | if response == .OK { 54 | let drop = AirDrop(files: picker.urls) 55 | drop.begin() 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | func beginDrop(_ providers: [NSItemProvider]) { 63 | assert(!Thread.isMainThread) 64 | guard let urls = providers.interfaceConvert() else { return } 65 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 66 | let drop = AirDrop(files: urls) 67 | drop.begin() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/DragDropView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragDropView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 2024. 10. 19.. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | 11 | class DragDropView: NSView { 12 | var onDragEntered: () -> Void = {} 13 | var onDragExited: () -> Void = {} 14 | var onDrop: () -> Void = {} 15 | 16 | override init(frame frameRect: NSRect) { 17 | super.init(frame: frameRect) 18 | registerForDraggedTypes([.fileURL]) 19 | } 20 | 21 | required init?(coder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 26 | onDragEntered() 27 | return .copy 28 | } 29 | 30 | override func draggingExited(_ sender: NSDraggingInfo?) { 31 | onDragExited() 32 | } 33 | 34 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 35 | onDrop() 36 | return true 37 | } 38 | } 39 | 40 | struct DragDropViewRepresentable: NSViewRepresentable { 41 | @Binding var isTargeted: Bool 42 | var onDrop: () -> Void 43 | 44 | func makeNSView(context: Context) -> DragDropView { 45 | let view = DragDropView() 46 | view.onDragEntered = { isTargeted = true } 47 | view.onDragExited = { isTargeted = false } 48 | view.onDrop = onDrop 49 | 50 | view.autoresizingMask = [.width, .height] 51 | 52 | return view 53 | } 54 | 55 | func updateNSView(_ nsView: DragDropView, context: Context) {} 56 | } 57 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/DropItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrayDrop+DropItem.swift 3 | // TrayDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import Cocoa 9 | import Foundation 10 | import QuickLook 11 | 12 | extension TrayDrop { 13 | struct DropItem: Identifiable, Codable, Equatable, Hashable { 14 | let id: UUID 15 | 16 | let fileName: String 17 | let size: Int 18 | 19 | let copiedDate: Date 20 | let workspacePreviewImageData: Data 21 | 22 | init(url: URL) throws { 23 | assert(!Thread.isMainThread) 24 | 25 | id = UUID() 26 | fileName = url.lastPathComponent 27 | 28 | size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0 29 | copiedDate = Date() 30 | workspacePreviewImageData = url.snapshotPreview().pngRepresentation 31 | 32 | try FileManager.default.createDirectory( 33 | at: storageURL.deletingLastPathComponent(), 34 | withIntermediateDirectories: true 35 | ) 36 | try FileManager.default.copyItem(at: url, to: storageURL) 37 | } 38 | } 39 | } 40 | 41 | extension TrayDrop.DropItem { 42 | static let mainDir = "CopiedItems" 43 | 44 | var storageURL: URL { 45 | documentsDirectory 46 | .appendingPathComponent(Self.mainDir) 47 | .appendingPathComponent(id.uuidString) 48 | .appendingPathComponent(fileName) 49 | } 50 | 51 | var workspacePreviewImage: NSImage { 52 | .init(data: workspacePreviewImageData) ?? .init() 53 | } 54 | 55 | var shouldClean: Bool { // TODO: In the future clean if old 56 | return false 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/DropItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrayDrop+DropItemView.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | import UniformTypeIdentifiers 11 | 12 | struct DropItemView: View { 13 | let item: TrayDrop.DropItem 14 | @EnvironmentObject var vm: DynamicIslandViewModel 15 | @ObservedObject var coordinator = DynamicIslandViewCoordinator.shared 16 | @ObservedObject var tvm = TrayDrop.shared 17 | 18 | @State var hover = false 19 | 20 | var body: some View { 21 | ZStack(alignment: .topTrailing) { 22 | VStack { 23 | RoundedRectangle(cornerRadius: 6) 24 | .fill(.clear) 25 | .background { 26 | Image(nsImage: item.workspacePreviewImage) 27 | .resizable() 28 | .aspectRatio(contentMode: .fit) 29 | .clipShape(RoundedRectangle(cornerRadius: 6)) 30 | } 31 | 32 | Text(item.fileName) 33 | .multilineTextAlignment(.center) 34 | .font(.footnote) 35 | .foregroundStyle(hover ? .white : .gray) 36 | .lineLimit(2) 37 | .allowsTightening(true) 38 | } 39 | .contentShape(Rectangle()) 40 | .onDrag { 41 | handleOnDrag(for: item) 42 | } 43 | .frame(width: 64, height: 64) 44 | 45 | if hover { 46 | Circle() 47 | .fill(.white) 48 | .overlay(Image(systemName: "xmark").foregroundStyle(.black).font(.system(size: 7)).fontWeight(.semibold)) 49 | .frame(width: spacing, height: spacing) 50 | .opacity(hover ? 1 : 0) // TODO: Use option key pressed to show delete 51 | .scaleEffect(coordinator.optionKeyPressed ? 1 : 0.5) 52 | .transition(.blurReplace.combined(with: .scale)) 53 | .offset(x: spacing / 2, y: -spacing / 2) 54 | .onTapGesture { tvm.delete(item.id) } 55 | .shadow(color: .black, radius: 3) 56 | } 57 | } 58 | .onHover { hovering in 59 | withAnimation(.smooth) { 60 | if hovering { 61 | hover.toggle() 62 | } else { 63 | hover.toggle() 64 | } 65 | } 66 | } 67 | } 68 | 69 | private func handleOnDrag(for item: TrayDrop.DropItem) -> NSItemProvider { 70 | guard let itemProvider = NSItemProvider(contentsOf: item.storageURL) else { 71 | return NSItemProvider() 72 | } 73 | 74 | let nameWithoutExtension = (item.fileName as NSString).deletingPathExtension 75 | itemProvider.suggestedName = nameWithoutExtension 76 | 77 | return itemProvider 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/Ext+FileProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ext+FileProvider.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import Cocoa 9 | import Foundation 10 | import UniformTypeIdentifiers 11 | 12 | extension NSItemProvider { 13 | private func duplicateToOurStorage(_ url: URL?) throws -> URL? { 14 | guard let url else { return nil } 15 | let temp = temporaryDirectory 16 | .appendingPathComponent("TemporaryDrop") 17 | .appendingPathComponent(UUID().uuidString) 18 | .appendingPathComponent(url.lastPathComponent) 19 | try? FileManager.default.createDirectory( 20 | at: temp.deletingLastPathComponent(), 21 | withIntermediateDirectories: true 22 | ) 23 | try FileManager.default.copyItem(at: url, to: temp) 24 | return temp 25 | } 26 | 27 | func convertToFilePathThatIsWhatWeThinkItWillWorkWithNotchDrop() -> URL? { 28 | var url: URL? 29 | let sem = DispatchSemaphore(value: 0) 30 | _ = loadObject(ofClass: URL.self) { item, _ in 31 | url = try? self.duplicateToOurStorage(item) 32 | sem.signal() 33 | } 34 | sem.wait() 35 | if url == nil { 36 | loadInPlaceFileRepresentation( 37 | forTypeIdentifier: UTType.data.identifier 38 | ) { input, _, _ in 39 | defer { sem.signal() } 40 | url = try? self.duplicateToOurStorage(input) 41 | } 42 | sem.wait() 43 | } 44 | return url 45 | } 46 | } 47 | 48 | extension [NSItemProvider] { 49 | func interfaceConvert() -> [URL]? { 50 | let urls = compactMap { provider -> URL? in 51 | provider.convertToFilePathThatIsWhatWeThinkItWillWorkWithNotchDrop() 52 | } 53 | guard urls.count == count else { 54 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 55 | NSAlert.popError(NSLocalizedString("One or more files failed to load", comment: "")) 56 | } 57 | return nil 58 | } 59 | return urls 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/Ext+NSAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ext+NSAlert.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/9. 6 | // 7 | 8 | import Cocoa 9 | 10 | extension NSAlert { 11 | static func popError(_ error: String) { 12 | let alert = NSAlert() 13 | alert.messageText = NSLocalizedString("Error", comment: "") 14 | alert.alertStyle = .critical 15 | alert.informativeText = error 16 | alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) 17 | alert.runModal() 18 | } 19 | 20 | static func popRestart(_ error: String, completion: @escaping () -> Void) { 21 | let alert = NSAlert() 22 | alert.messageText = NSLocalizedString("Need Restart", comment: "") 23 | alert.alertStyle = .critical 24 | alert.informativeText = error 25 | alert.addButton(withTitle: NSLocalizedString("Exit", comment: "")) 26 | alert.addButton(withTitle: NSLocalizedString("Later", comment: "")) 27 | let response = alert.runModal() 28 | if response == .alertFirstButtonReturn { 29 | completion() 30 | } 31 | } 32 | 33 | static func popError(_ error: Error) { 34 | popError(error.localizedDescription) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/Ext+NSImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ext+NSImage.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import Cocoa 9 | 10 | extension NSImage { 11 | var pngRepresentation: Data { 12 | guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else { 13 | return .init() 14 | } 15 | let imageRep = NSBitmapImageRep(cgImage: cgImage) 16 | imageRep.size = size 17 | return imageRep.representation(using: .png, properties: [:]) ?? .init() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/Ext+URL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ext+URL.swift 3 | // NotchDrop 4 | // 5 | // Created by 秋星桥 on 2024/7/8. 6 | // 7 | 8 | import Cocoa 9 | import Foundation 10 | import QuickLook 11 | 12 | extension URL { 13 | func snapshotPreview() -> NSImage { 14 | if let preview = QLThumbnailImageCreate( 15 | kCFAllocatorDefault, 16 | self as CFURL, 17 | CGSize(width: 128, height: 128), 18 | nil 19 | )?.takeRetainedValue() { 20 | return NSImage(cgImage: preview, size: .zero) 21 | } 22 | return NSWorkspace.shared.icon(forFile: path) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DynamicIsland/components/Shelf/TrayDrop.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import Combine 3 | import Foundation 4 | import OrderedCollections 5 | 6 | class TrayDrop: ObservableObject { 7 | static let shared = TrayDrop() 8 | 9 | @Published var items: OrderedSet 10 | var isEmpty: Bool { items.isEmpty } 11 | @Published var isLoading: Int = 0 12 | 13 | init(items: OrderedSet = .init(), isLoading: Int = 0) { 14 | self.items = items 15 | self.isLoading = isLoading 16 | } 17 | 18 | func load(_ providers: [NSItemProvider]) { 19 | assert(!Thread.isMainThread) 20 | DispatchQueue.main.asyncAndWait { isLoading += 1 } 21 | 22 | guard let urls = providers.interfaceConvert() else { 23 | DispatchQueue.main.asyncAndWait { isLoading -= 1 } 24 | print("Faield to load items") 25 | return 26 | } 27 | let dropItems = urls.map { url in 28 | try? DropItem(url: url) 29 | }.compactMap { $0 } 30 | 31 | DispatchQueue.main.async { 32 | dropItems.forEach { self.items.updateOrInsert($0, at: 0) } 33 | self.isLoading -= 1 34 | } 35 | print("DONE") 36 | } 37 | 38 | func cleanExpiredFiles() { 39 | var inEdit = items 40 | let shouldCleanItems = items.filter(\.shouldClean) 41 | for item in shouldCleanItems { 42 | inEdit.remove(item) 43 | } 44 | items = inEdit 45 | } 46 | 47 | func delete(_ item: DropItem.ID) { 48 | guard let item = items.first(where: { $0.id == item }) else { return } 49 | delete(item: item) 50 | } 51 | 52 | private func delete(item: DropItem) { 53 | var inEdit = items 54 | 55 | var url = item.storageURL 56 | try? FileManager.default.removeItem(at: url) 57 | 58 | do { 59 | // loops up to the main directory 60 | url = url.deletingLastPathComponent() 61 | while url.lastPathComponent != DropItem.mainDir, url != documentsDirectory { 62 | let contents = try FileManager.default.contentsOfDirectory(atPath: url.path) 63 | guard contents.isEmpty else { break } 64 | try FileManager.default.removeItem(at: url) 65 | url = url.deletingLastPathComponent() 66 | } 67 | } catch {} 68 | 69 | inEdit.remove(item) 70 | items = inEdit 71 | } 72 | 73 | func removeAll() { 74 | items.forEach { delete(item: $0) } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /DynamicIsland/components/Stats/DetailedTimelineGraph.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/components/Stats/DetailedTimelineGraph.swift -------------------------------------------------------------------------------- /DynamicIsland/components/Stats/DualTimelineGraph.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/components/Stats/DualTimelineGraph.swift -------------------------------------------------------------------------------- /DynamicIsland/components/Tabs/TabButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabButton.swift 3 | // DynamicIsland 4 | // 5 | // Created by Hugo Persson on 2024-08-24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TabButton: View { 11 | let label: String 12 | let icon: String 13 | let selected: Bool 14 | let onClick: () -> Void 15 | 16 | var body: some View { 17 | Button(action: onClick) { 18 | Image(systemName: icon) 19 | .padding(.horizontal, 15) 20 | .contentShape(Capsule()) 21 | } 22 | .buttonStyle(PlainButtonStyle()) 23 | } 24 | } 25 | 26 | #Preview { 27 | TabButton(label: "Home", icon: "tray.fill", selected: true) { 28 | print("Tapped") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DynamicIsland/components/Tabs/TabSelectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabSelectionView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Hugo Persson on 2024-08-25. 6 | // Modified by Hariharan Mudaliar 7 | 8 | import SwiftUI 9 | import Defaults 10 | 11 | struct TabModel: Identifiable { 12 | let id = UUID() 13 | let label: String 14 | let icon: String 15 | let view: NotchViews 16 | } 17 | 18 | struct TabSelectionView: View { 19 | @ObservedObject var coordinator = DynamicIslandViewCoordinator.shared 20 | @Default(.enableTimerFeature) var enableTimerFeature 21 | @Default(.enableStatsFeature) var enableStatsFeature 22 | @Default(.enableColorPickerFeature) var enableColorPickerFeature 23 | @Namespace var animation 24 | 25 | private var tabs: [TabModel] { 26 | var tabsArray: [TabModel] = [] 27 | 28 | tabsArray.append(TabModel(label: "Home", icon: "house.fill", view: .home)) 29 | 30 | tabsArray.append(TabModel(label: "Shelf", icon: "tray.fill", view: .shelf)) 31 | 32 | // Timer tab only shown when timer feature is enabled 33 | if Defaults[.enableTimerFeature] { 34 | tabsArray.append(TabModel(label: "Timer", icon: "timer", view: .timer)) 35 | } 36 | 37 | // Stats tab only shown when stats feature is enabled 38 | if Defaults[.enableStatsFeature] { 39 | tabsArray.append(TabModel(label: "Stats", icon: "chart.xyaxis.line", view: .stats)) 40 | } 41 | 42 | return tabsArray 43 | } 44 | var body: some View { 45 | HStack(spacing: 0) { 46 | ForEach(tabs) { tab in 47 | TabButton(label: tab.label, icon: tab.icon, selected: coordinator.currentView == tab.view) { 48 | withAnimation(.smooth) { 49 | coordinator.currentView = tab.view 50 | } 51 | } 52 | .frame(height: 26) 53 | .foregroundStyle(tab.view == coordinator.currentView ? .white : .gray) 54 | .background { 55 | if tab.view == coordinator.currentView { 56 | Capsule() 57 | .fill(coordinator.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) 58 | .matchedGeometryEffect(id: "capsule", in: animation) 59 | } else { 60 | Capsule() 61 | .fill(coordinator.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) 62 | .matchedGeometryEffect(id: "capsule", in: animation) 63 | .hidden() 64 | } 65 | } 66 | } 67 | } 68 | .clipShape(Capsule()) 69 | } 70 | } 71 | 72 | #Preview { 73 | DynamicIslandHeader().environmentObject(DynamicIslandViewModel()) 74 | } 75 | -------------------------------------------------------------------------------- /DynamicIsland/components/TestView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 14/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FluidSlider: View { 11 | private let color: Color = Color.white 12 | @State private var offset: CGFloat = 0 13 | var rectSize = CGSize(width: 300, height: 50) 14 | var rectSize2 = CGSize(width: 200, height: 18) 15 | var circleSize: CGFloat = 35 16 | @GestureState var isDragging: Bool = false 17 | @State var previousOffset: CGFloat = 0 18 | @State private var isBeating: Bool = false 19 | 20 | var body: some View { 21 | HStack { 22 | slider 23 | .frame(width: rectSize2.width, height: circleSize) 24 | } 25 | .padding() 26 | .background(.black) 27 | } 28 | 29 | private var slider: some View { 30 | ZStack { 31 | Canvas { context, size in 32 | context.addFilter(.alphaThreshold(min: 0.5, max: 1, color: color)) 33 | context.addFilter(.blur(radius: 10)) 34 | 35 | context.drawLayer { ctx in 36 | if let rectangle = ctx.resolveSymbol(id: "Capsule") { 37 | ctx.draw(rectangle, at: CGPoint(x: size.width/2, y: size.height/2)) 38 | } 39 | if let circle = ctx.resolveSymbol(id: "Circle") { 40 | ctx.draw(circle, at: CGPoint(x: size.width/2 - rectSize2.width/2 + circleSize/2, y: size.height/2)) 41 | } 42 | } 43 | } symbols: { 44 | Capsule() 45 | .frame(width: rectSize2.width, height: rectSize2.height, alignment: .center) 46 | .tag("Capsule") 47 | 48 | Circle() 49 | .frame(width: circleSize, height: circleSize, alignment: .center) 50 | .offset(x: offset) 51 | .animation(.spring(), value: isDragging) 52 | .tag("Circle") 53 | } 54 | .simultaneousGesture( 55 | DragGesture(minimumDistance: 0) 56 | .updating($isDragging, body: { _, state, _ in 57 | state = true 58 | }) 59 | .onChanged({ value in 60 | self.offset = min(max(self.previousOffset + value.translation.width, 0), rectSize2.width - circleSize) 61 | }) 62 | .onEnded({ value in 63 | self.previousOffset = self.offset 64 | }) 65 | ) 66 | Circle() 67 | .fill(Color.black) 68 | .frame(width: circleSize * 0.6) 69 | .overlay { 70 | Image(systemName: "speaker.wave.2.fill") 71 | .imageScale(.small) 72 | } 73 | .offset(x: (-rectSize2.width/2) + (circleSize/2)) 74 | .offset(x: offset) 75 | .animation(.spring(), value: isDragging) 76 | .allowsHitTesting(false) 77 | } 78 | } 79 | 80 | 81 | private var animation: Animation { 82 | .spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0.5) 83 | } 84 | 85 | private var percentage: Int { 86 | Int((offset) / (rectSize.width - circleSize) * 100) 87 | } 88 | } 89 | 90 | #Preview { 91 | FluidSlider() 92 | } 93 | -------------------------------------------------------------------------------- /DynamicIsland/components/Timer/TimerLiveActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerLiveActivity.swift 3 | // DynamicIsland 4 | // 5 | // Created by Ebullioscopic on 2025-01-13. 6 | // 7 | 8 | import SwiftUI 9 | import Defaults 10 | 11 | struct TimerLiveActivity: View { 12 | @EnvironmentObject var vm: DynamicIslandViewModel 13 | @ObservedObject var coordinator = DynamicIslandViewCoordinator.shared 14 | @ObservedObject var timerManager = TimerManager.shared 15 | @State private var isHovering: Bool = false 16 | @State private var gestureProgress: CGFloat = 0 17 | 18 | var body: some View { 19 | HStack { 20 | // Left side - Timer icon animation 21 | timerIconSection 22 | 23 | // Center - Expandable timer info (similar to music banner) 24 | timerInfoSection 25 | 26 | // Right side - Timer countdown text 27 | timerCountdownSection 28 | } 29 | .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) 30 | } 31 | 32 | private var timerIconSection: some View { 33 | HStack { 34 | // Simple timer icon with color, no background circle or animations 35 | Image(systemName: "timer") 36 | .font(.system(size: 16, weight: .medium)) 37 | .foregroundStyle(timerManager.timerColor) 38 | .frame(width: 24, height: 24) 39 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), 40 | height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) 41 | } 42 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), 43 | height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12))) 44 | } 45 | 46 | private var timerInfoSection: some View { 47 | Rectangle() 48 | .fill(.black) 49 | .frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0)) 50 | } 51 | 52 | private var timerCountdownSection: some View { 53 | HStack { 54 | VStack(spacing: 2) { 55 | // Remaining time 56 | Text(timerManager.formattedRemainingTime()) 57 | .font(.system(size: 11, weight: .medium, design: .monospaced)) 58 | .foregroundColor(timerManager.isOvertime ? .red : .white) 59 | 60 | // Progress indicator 61 | if timerManager.totalDuration > 0 { 62 | ProgressView(value: timerManager.progress) 63 | .progressViewStyle(LinearProgressViewStyle(tint: timerManager.timerColor)) 64 | .frame(width: 30, height: 2) 65 | } 66 | } 67 | .frame(maxWidth: .infinity) 68 | } 69 | .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), 70 | height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) 71 | } 72 | } 73 | 74 | #Preview { 75 | TimerLiveActivity() 76 | .environmentObject(DynamicIslandViewModel()) 77 | .frame(width: 300, height: 32) 78 | .background(.black) 79 | .onAppear { 80 | // Start a demo timer for preview 81 | TimerManager.shared.startDemoTimer(duration: 300) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DynamicIsland/components/Tips/TipStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TipStore.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 15/09/2024. 6 | // 7 | 8 | import SwiftUI 9 | import TipKit 10 | 11 | struct HUDsTip: Tip { 12 | var title: Text { 13 | Text("Enhance your experience with HUDs") 14 | } 15 | 16 | 17 | var message: Text? { 18 | Text("Unlock advanced features and improve your experience. Upgrade now for more customizations!") 19 | } 20 | 21 | 22 | var image: Image? { 23 | AppIcon(for: "dynamicisland.DynamicIsland") 24 | } 25 | 26 | var actions: [Action] { 27 | Action { 28 | Text("More") 29 | } 30 | } 31 | } 32 | 33 | struct CBTip: Tip { 34 | var title: Text { 35 | Text("Boost your productivity with Clipboard Manager") 36 | } 37 | 38 | 39 | var message: Text? { 40 | Text("Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!") 41 | } 42 | 43 | 44 | var image: Image? { 45 | AppIcon(for: "dynamicisland.DynamicIsland") 46 | } 47 | 48 | var actions: [Action] { 49 | Action { 50 | Text("More") 51 | } 52 | } 53 | } 54 | 55 | struct TipsView: View { 56 | var hudTip = HUDsTip() 57 | var cbTip = CBTip() 58 | var body: some View { 59 | VStack { 60 | TipView(hudTip) 61 | TipView(cbTip) 62 | } 63 | .task { 64 | try? Tips.configure([ 65 | .displayFrequency(.immediate), 66 | .datastoreLocation(.applicationDefault) 67 | ]) 68 | } 69 | } 70 | } 71 | 72 | #Preview { 73 | TipsView() 74 | } 75 | -------------------------------------------------------------------------------- /DynamicIsland/components/WhatsNewView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WhatsNewView.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 09/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WhatsNewView: View { 11 | @Binding var isPresented: Bool 12 | 13 | var body: some View { 14 | VStack(spacing: 20) { 15 | Text("What's New") 16 | .font(.largeTitle) 17 | 18 | VStack(alignment: .leading, spacing: 10) { 19 | Text("• New feature 1") 20 | Text("• Improved performance") 21 | Text("• Bug fixes") 22 | } 23 | 24 | Button("Got it!") { 25 | isPresented = false 26 | } 27 | } 28 | .frame(width: 300, height: 200) 29 | .padding() 30 | } 31 | } 32 | 33 | #Preview { 34 | WhatsNewView(isPresented: .constant(true)) 35 | } 36 | -------------------------------------------------------------------------------- /DynamicIsland/dynamic.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/dynamic.m4a -------------------------------------------------------------------------------- /DynamicIsland/enums/generic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // generic.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // Modified by Hariharan Mudaliar 7 | 8 | import Foundation 9 | import Defaults 10 | 11 | public enum Style { 12 | case notch 13 | case floating 14 | } 15 | 16 | public enum ContentType: Int, Codable, Hashable, Equatable { 17 | case normal 18 | case menu 19 | case settings 20 | } 21 | 22 | public enum NotchState { 23 | case closed 24 | case open 25 | } 26 | 27 | public enum NotchViews { 28 | case home 29 | case shelf 30 | case timer 31 | case stats 32 | case colorPicker 33 | } 34 | 35 | enum SettingsEnum { 36 | case general 37 | case about 38 | case charge 39 | case download 40 | case mediaPlayback 41 | case hud 42 | case shelf 43 | case extensions 44 | } 45 | 46 | enum DownloadIndicatorStyle: String, Defaults.Serializable { 47 | case progress = "Progress" 48 | case percentage = "Percentage" 49 | } 50 | 51 | enum DownloadIconStyle: String, Defaults.Serializable { 52 | case onlyAppIcon = "Only app icon" 53 | case onlyIcon = "Only download icon" 54 | case iconAndAppIcon = "Icon and app icon" 55 | } 56 | 57 | enum MirrorShapeEnum: String, Defaults.Serializable { 58 | case rectangle = "Rectangular" 59 | case circle = "Circular" 60 | } 61 | 62 | enum WindowHeightMode: String, Defaults.Serializable { 63 | case matchMenuBar = "Match menubar height" 64 | case matchRealNotchSize = "Match real notch height" 65 | case custom = "Custom height" 66 | } 67 | 68 | enum SliderColorEnum: String, CaseIterable, Defaults.Serializable { 69 | case white = "White" 70 | case albumArt = "Match album art" 71 | case accent = "Accent color" 72 | } 73 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/ActionBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionBar.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 15/09/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | func actionBar(padding: CGFloat = 10, @ViewBuilder content: () -> Content) -> some View { 12 | self 13 | .padding(.bottom, 24) 14 | .overlay(alignment: .bottom) { 15 | VStack(spacing: -1) { 16 | Divider() 17 | HStack(spacing: 0) { 18 | content() 19 | .buttonStyle(PlainButtonStyle()) 20 | } 21 | .frame(height: 16) 22 | .padding(.vertical, 4) 23 | .padding(.horizontal, padding) 24 | .frame(maxWidth: .infinity, alignment: .leading) 25 | } 26 | .frame(height: 24) 27 | .background(.separator) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/BundleInfos.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BundleInfos.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 08/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Bundle { 11 | var releaseVersionNumber: String? { 12 | return infoDictionary?["CFBundleShortVersionString"] as? String 13 | } 14 | var buildVersionNumber: String? { 15 | return infoDictionary?["CFBundleVersion"] as? String 16 | } 17 | var releaseVersionNumberPretty: String { 18 | return "v\(releaseVersionNumber ?? "1.0.0")" 19 | } 20 | 21 | var iconFileName: String? { 22 | guard let icons = infoDictionary?["CFBundleIcons"] as? [String: Any], 23 | let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any], 24 | let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String], 25 | let iconFileName = iconFiles.last 26 | else { return nil } 27 | return iconFileName 28 | } 29 | } 30 | 31 | struct BundleAppIcon: View { 32 | var body: some View { 33 | Bundle.main.iconFileName 34 | .flatMap { NSImage(named: $0) } 35 | .map { Image(nsImage: $0) } 36 | } 37 | } 38 | 39 | func isNewVersion() -> Bool { 40 | let defaults = UserDefaults.standard 41 | let currentVersion = Bundle.main.releaseVersionNumber ?? "1.0" 42 | let savedVersion = defaults.string(forKey: "LastVersionRun") ?? "" 43 | 44 | if currentVersion != savedVersion { 45 | defaults.set(currentVersion, forKey: "LastVersionRun") 46 | return true 47 | } 48 | return false 49 | } 50 | 51 | func isExtensionRunning(_ bundleID: String) -> Bool { 52 | if let _ = NSWorkspace.shared.runningApplications.first(where: {$0.bundleIdentifier == bundleID}) { 53 | return true 54 | } 55 | 56 | return false 57 | } 58 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/Button+Bouncing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Button+Bouncing.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 19/08/24. 6 | // 7 | import SwiftUI 8 | import Defaults 9 | 10 | struct BouncingButtonStyle: ButtonStyle { 11 | let vm: DynamicIslandViewModel 12 | @State private var isPressed = false 13 | 14 | func makeBody(configuration: Configuration) -> some View { 15 | configuration.label 16 | .padding(12) 17 | .background( 18 | RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? 10 : MusicPlayerImageSizes.cornerRadiusInset.closed) 19 | .fill(Color(red: 20/255, green: 20/255, blue: 20/255)) 20 | .strokeBorder(.white.opacity(0.04), lineWidth: 1) 21 | ) 22 | .scaleEffect(isPressed ? 0.9 : 1.0) 23 | .onChange(of: configuration.isPressed) { _, _ in 24 | withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) { 25 | isPressed.toggle() 26 | } 27 | } 28 | } 29 | } 30 | 31 | extension Button { 32 | func bouncingStyle(vm: DynamicIslandViewModel) -> some View { 33 | self.buttonStyle(BouncingButtonStyle(vm: vm)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/ConditionalModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConditionalModifier.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 20/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | @ViewBuilder func conditionalModifier(_ condition: Bool, transform: (Self) -> Content) -> some View { 12 | if condition { 13 | transform(self) 14 | } else { 15 | self 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/DataTypes+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataTypes+Extensions.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 27/08/24. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | extension Date { 13 | static var yesterday: Date { return Date().dayBefore } 14 | static var tomorrow: Date { return Date().dayAfter } 15 | var dayBefore: Date { 16 | return Calendar.current.date(byAdding: .day, value: -1, to: noon)! 17 | } 18 | var dayAfter: Date { 19 | return Calendar.current.date(byAdding: .day, value: 1, to: noon)! 20 | } 21 | var noon: Date { 22 | return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! 23 | } 24 | 25 | var date: String { 26 | let dateFormatter = DateFormatter() 27 | dateFormatter.dateFormat = "dd" 28 | return dateFormatter.string(from: self) 29 | } 30 | 31 | var month: String { 32 | let dateFormatter = DateFormatter() 33 | dateFormatter.dateFormat = "MMMM" 34 | return dateFormatter.string(from: self) 35 | } 36 | 37 | func dayOfTheWeek(dayOfWeek: Int) -> String { 38 | let dateFormatter = DateFormatter() 39 | dateFormatter.dateFormat = "EEE" 40 | let date = Calendar.current.date(bySetting: .weekday, value: dayOfWeek, of: self) ?? self 41 | return dateFormatter.string(from: date) 42 | } 43 | } 44 | 45 | extension NSSize { 46 | var s: String { "\(width.i)×\(height.i)" } 47 | 48 | var aspectRatio: Double { 49 | width / height 50 | } 51 | func scaled(by factor: Double) -> CGSize { 52 | CGSize(width: (width * factor).evenInt, height: (height * factor).evenInt) 53 | } 54 | 55 | } 56 | 57 | extension Int { 58 | var s: String { 59 | String(self) 60 | } 61 | var d: Double { 62 | Double(self) 63 | } 64 | } 65 | 66 | extension Double { 67 | @inline(__always) @inlinable var intround: Int { 68 | rounded().i 69 | } 70 | 71 | @inline(__always) @inlinable var i: Int { 72 | Int(self) 73 | } 74 | 75 | var evenInt: Int { 76 | let x = intround 77 | return x + x % 2 78 | } 79 | } 80 | 81 | extension CGFloat { 82 | @inline(__always) @inlinable var intround: Int { 83 | rounded().i 84 | } 85 | 86 | @inline(__always) @inlinable var i: Int { 87 | Int(self) 88 | } 89 | 90 | var evenInt: Int { 91 | let x = intround 92 | return x + x % 2 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/KeyboardShortcutsHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardShortcutsHelper.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 16/08/2024. 6 | // 7 | 8 | import KeyboardShortcuts 9 | import SwiftUI 10 | import Carbon 11 | 12 | 13 | extension View { 14 | 15 | public func keyboardShortcut(_ shortcut: KeyboardShortcuts.Name) -> some View { 16 | if let shortcut = shortcut.shortcut { 17 | if let keyEquivalent = shortcut.toKeyEquivalent() { 18 | return AnyView(self.keyboardShortcut(keyEquivalent, modifiers: shortcut.toEventModifiers())) 19 | } 20 | } 21 | 22 | return AnyView(self) 23 | } 24 | 25 | } 26 | 27 | extension KeyboardShortcuts.Shortcut { 28 | 29 | func toKeyEquivalent() -> KeyEquivalent? { 30 | let carbonKeyCode = UInt16(self.carbonKeyCode) 31 | let maxNameLength = 4 32 | var nameBuffer = [UniChar](repeating: 0, count : maxNameLength) 33 | var nameLength = 0 34 | 35 | let modifierKeys = UInt32(alphaLock >> 8) & 0xFF // Caps Lock 36 | var deadKeys: UInt32 = 0 37 | let keyboardType = UInt32(LMGetKbdType()) 38 | 39 | let source = TISCopyCurrentKeyboardLayoutInputSource().takeRetainedValue() 40 | guard let ptr = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else { 41 | NSLog("Could not get keyboard layout data") 42 | return nil 43 | } 44 | let layoutData = Unmanaged.fromOpaque(ptr).takeUnretainedValue() as Data 45 | let osStatus = layoutData.withUnsafeBytes { 46 | UCKeyTranslate($0.bindMemory(to: UCKeyboardLayout.self).baseAddress, carbonKeyCode, UInt16(kUCKeyActionDown), 47 | modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask), 48 | &deadKeys, maxNameLength, &nameLength, &nameBuffer) 49 | } 50 | guard osStatus == noErr else { 51 | NSLog("Code: 0x%04X Status: %+i", carbonKeyCode, osStatus); 52 | return nil 53 | } 54 | 55 | return KeyEquivalent(Character(String(utf16CodeUnits: nameBuffer, count: nameLength))) 56 | } 57 | 58 | func toEventModifiers() -> SwiftUI.EventModifiers { 59 | var modifiers: SwiftUI.EventModifiers = [] 60 | 61 | if self.modifiers.contains(NSEvent.ModifierFlags.command) { 62 | modifiers.update(with: EventModifiers.command) 63 | } 64 | 65 | if self.modifiers.contains(NSEvent.ModifierFlags.control) { 66 | modifiers.update(with: EventModifiers.control) 67 | } 68 | 69 | if self.modifiers.contains(NSEvent.ModifierFlags.option) { 70 | modifiers.update(with: EventModifiers.option) 71 | } 72 | 73 | if self.modifiers.contains(NSEvent.ModifierFlags.shift) { 74 | modifiers.update(with: EventModifiers.shift) 75 | } 76 | 77 | if self.modifiers.contains(NSEvent.ModifierFlags.capsLock) { 78 | modifiers.update(with: EventModifiers.capsLock) 79 | } 80 | 81 | if self.modifiers.contains(NSEvent.ModifierFlags.numericPad) { 82 | modifiers.update(with: EventModifiers.numericPad) 83 | } 84 | 85 | return modifiers 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /DynamicIsland/extensions/MouseTracker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MouseTracker.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 12/08/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension NSScreen { 11 | static var screenWithMouse: NSScreen? { 12 | let mouseLocation = NSEvent.mouseLocation 13 | let screens = NSScreen.screens 14 | let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) }) 15 | 16 | return screenWithMouse 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/AppIcons.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppIcons.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 16/08/24. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | 11 | struct AppIcons { 12 | 13 | func getIcon(file path: String) -> NSImage? { 14 | guard FileManager.default.fileExists(atPath: path) 15 | else { return nil } 16 | 17 | return NSWorkspace.shared.icon(forFile: path) 18 | } 19 | 20 | func getIcon(bundleID: String) -> NSImage? { 21 | guard let path = NSWorkspace.shared.urlForApplication( 22 | withBundleIdentifier: bundleID 23 | )?.absoluteString 24 | else { return nil } 25 | 26 | return getIcon(file: path) 27 | } 28 | 29 | /// Easily read Info.plist as a Dictionary from any bundle by accessing .infoDictionary on Bundle 30 | func bundle(forBundleID: String) -> Bundle? { 31 | guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: forBundleID) 32 | else { return nil } 33 | 34 | return Bundle(url: url) 35 | } 36 | 37 | } 38 | 39 | func AppIcon(for bundleID: String) -> Image { 40 | let workspace = NSWorkspace.shared 41 | 42 | if let appURL = workspace.urlForApplication(withBundleIdentifier: bundleID) { 43 | let appIcon = workspace.icon(forFile: appURL.path) 44 | return Image(nsImage: appIcon) 45 | } 46 | 47 | return Image(nsImage: workspace.icon(for: .applicationBundle)) 48 | } 49 | 50 | 51 | func AppIconAsNSImage(for bundleID: String) -> NSImage? { 52 | let workspace = NSWorkspace.shared 53 | 54 | if let appURL = workspace.urlForApplication(withBundleIdentifier: bundleID) { 55 | let appIcon = workspace.icon(forFile: appURL.path) 56 | return appIcon 57 | } 58 | return nil 59 | } 60 | 61 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/AppleScriptError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleScriptError.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | enum AppleScriptError: Error { 12 | case initScriptFailed 13 | case runtimeError 14 | case emptyOutput 15 | } -------------------------------------------------------------------------------- /DynamicIsland/helpers/AppleScriptHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleScriptHelper.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-03-29. 6 | // 7 | 8 | import Foundation 9 | 10 | class AppleScriptHelper { 11 | @discardableResult 12 | class func execute(_ scriptText: String) async throws -> NSAppleEventDescriptor? { 13 | try await withCheckedThrowingContinuation { continuation in 14 | Task.detached(priority: .userInitiated) { 15 | let script = NSAppleScript(source: scriptText) 16 | var error: NSDictionary? 17 | if let descriptor = script?.executeAndReturnError(&error) { 18 | continuation.resume(returning: descriptor) 19 | } else if let error = error { 20 | continuation.resume(throwing: NSError(domain: "AppleScriptError", code: 1, userInfo: error as? [String: Any])) 21 | } else { 22 | continuation.resume(throwing: NSError(domain: "AppleScriptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unknown error"])) 23 | } 24 | } 25 | } 26 | } 27 | 28 | class func executeVoid(_ scriptText: String) async throws { 29 | _ = try await execute(scriptText) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/AppleScriptRunner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleScriptRunner.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | class AppleScriptRunner { 12 | private init() {} 13 | 14 | static func run(script: String) throws -> String { 15 | var error: NSDictionary? 16 | if let scriptObject = NSAppleScript(source: script) { 17 | let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(&error) 18 | guard error == nil else { 19 | throw AppleScriptError.runtimeError 20 | } 21 | if let outputString = output.stringValue { 22 | return outputString 23 | } 24 | throw AppleScriptError.emptyOutput 25 | } 26 | throw AppleScriptError.initScriptFailed 27 | } 28 | } -------------------------------------------------------------------------------- /DynamicIsland/helpers/AudioPlayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AudioPlayer.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 09/08/24. 6 | // 7 | 8 | import Foundation 9 | import AppKit 10 | 11 | class AudioPlayer { 12 | func play(fileName: String, fileExtension: String) { 13 | NSSound(contentsOf:Bundle.main.url(forResource: fileName, withExtension: fileExtension)!, byReference: false)?.play() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/Clipboard+Content.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | 3 | func getAttributedString(content: Any, type: NSPasteboard.PasteboardType) -> NSAttributedString? { 4 | if let stringContent = content as? String { 5 | return NSAttributedString(string: stringContent) 6 | } 7 | if type == .rtf, let data = content as? Data { 8 | return NSAttributedString(rtf: data, documentAttributes: nil) 9 | } else if type == .html, let data = content as? Data { 10 | return try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) 11 | } else if type.rawValue == "public.utf8-plain-text", let data = content as? Data { 12 | return try? NSAttributedString(data: data, documentAttributes: nil) 13 | } else if type == .string { 14 | return NSAttributedString(string: content as? String ?? "") 15 | } else if type == .fileURL { 16 | return NSAttributedString(string: content as? String ?? "") 17 | } else if type == NSPasteboard.PasteboardType("com.apple.webarchive"), let data = content as? Data { 18 | return try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.webArchive], documentAttributes: nil) 19 | } 20 | return nil 21 | } 22 | 23 | func isText(type: NSPasteboard.PasteboardType) -> Bool { 24 | return type == .string || type == .html || type == .rtf || type == .html || type == .string || type.rawValue == "public.utf8-plain-text" || type.rawValue == "public.utf16-external-plain-text" || type.rawValue == "com.apple.webarchive" 25 | } 26 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/MediaChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MediaChecker.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-07-26. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | final class MediaChecker: Sendable { 12 | enum MediaCheckerError: Error { 13 | case missingResources 14 | case processExecutionFailed 15 | case timeout 16 | } 17 | 18 | func checkDeprecationStatus() async throws -> Bool { 19 | guard let scriptURL = Bundle.main.url(forResource: "mediaremote-adapter", withExtension: "pl"), 20 | let nowPlayingTestClientPath = Bundle.main.url(forResource: "MediaRemoteAdapterTestClient", withExtension: nil)?.path, 21 | let frameworkPath = Bundle.main.privateFrameworksPath?.appending("/MediaRemoteAdapter.framework") 22 | else { 23 | throw MediaCheckerError.missingResources 24 | } 25 | 26 | let process = Process() 27 | process.executableURL = URL(fileURLWithPath: "/usr/bin/perl") 28 | process.arguments = [scriptURL.path, frameworkPath, nowPlayingTestClientPath, "test"] 29 | 30 | do { 31 | try process.run() 32 | } catch { 33 | throw MediaCheckerError.processExecutionFailed 34 | } 35 | 36 | // Timeout after 10 seconds 37 | let didExit: Bool = try await withThrowingTaskGroup(of: Bool.self) { group in 38 | group.addTask { 39 | process.waitUntilExit() 40 | return true 41 | } 42 | group.addTask { 43 | try await Task.sleep(for: .seconds(10)) 44 | if process.isRunning { 45 | process.terminate() 46 | } 47 | return false 48 | } 49 | for try await exited in group { 50 | if exited { 51 | group.cancelAll() 52 | return true 53 | } 54 | } 55 | throw MediaCheckerError.timeout 56 | } 57 | 58 | if !didExit { 59 | throw MediaCheckerError.timeout 60 | } 61 | 62 | let isDeprecated = process.terminationStatus == 1 63 | return isDeprecated 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /DynamicIsland/helpers/SensorError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorError.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SensorError: Error { 12 | enum Display: Error { 13 | case notFound 14 | case notSilicon 15 | case notStandard 16 | } 17 | enum Keyboard: Error { 18 | case notFound 19 | case notSilicon 20 | case notStandard 21 | } 22 | } -------------------------------------------------------------------------------- /DynamicIsland/helpers/SensorMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorMethod.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SensorMethod { 12 | case standard 13 | case m1 14 | case allFailed 15 | } -------------------------------------------------------------------------------- /DynamicIsland/helpers/SystemHUDDebugger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemHUDDebugger.swift 3 | // DynamicIsland 4 | // 5 | // Debug utility for System HUD functionality 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | class SystemHUDDebugger { 12 | 13 | /// Test system HUD functionality and print status 14 | public static func testSystemHUD() { 15 | print("\n🔍 === System HUD Debug Report ===") 16 | 17 | // Check current OSDUIHelper status 18 | let isRunning = SystemOSDManager.isOSDUIHelperRunning() 19 | print("📊 OSDUIHelper Status: \(isRunning ? "✅ Running" : "❌ Not running")") 20 | 21 | // Test disable 22 | print("🔇 Testing disable...") 23 | SystemOSDManager.disableSystemHUD() 24 | 25 | // Wait and check 26 | usleep(500000) 27 | let isRunningAfterDisable = SystemOSDManager.isOSDUIHelperRunning() 28 | print("📊 After disable: \(isRunningAfterDisable ? "✅ Running (stopped)" : "❌ Not running")") 29 | 30 | // Test enable 31 | print("🔊 Testing re-enable...") 32 | SystemOSDManager.enableSystemHUD() 33 | 34 | // Wait and check 35 | usleep(1000000) 36 | let isRunningAfterEnable = SystemOSDManager.isOSDUIHelperRunning() 37 | print("📊 After re-enable: \(isRunningAfterEnable ? "✅ Running" : "❌ Not running")") 38 | 39 | print("🔍 === End Debug Report ===\n") 40 | 41 | if !isRunningAfterEnable { 42 | print("⚠️ WARNING: System HUD may not be working properly!") 43 | print("💡 Try pressing volume keys to test system HUD functionality") 44 | } 45 | } 46 | 47 | /// Force restart OSDUIHelper using multiple methods 48 | public static func forceRestartOSDUIHelper() { 49 | print("🔄 Force restarting OSDUIHelper...") 50 | 51 | // Method 1: Kill and kickstart 52 | SystemOSDManager.enableSystemHUD() 53 | 54 | // Method 2: Try launchctl bootstrap (more aggressive) 55 | do { 56 | let bootstrap = Process() 57 | bootstrap.executableURL = URL(fileURLWithPath: "/bin/launchctl") 58 | bootstrap.arguments = ["bootstrap", "gui/\(getuid())", "/System/Library/LaunchAgents/com.apple.OSDUIHelper.plist"] 59 | try bootstrap.run() 60 | bootstrap.waitUntilExit() 61 | print("✅ Bootstrap method completed") 62 | } catch { 63 | print("❌ Bootstrap method failed: \(error)") 64 | } 65 | 66 | // Method 3: Try direct service restart 67 | do { 68 | let restart = Process() 69 | restart.executableURL = URL(fileURLWithPath: "/bin/launchctl") 70 | restart.arguments = ["restart", "gui/\(getuid())/com.apple.OSDUIHelper"] 71 | try restart.run() 72 | restart.waitUntilExit() 73 | print("✅ Restart method completed") 74 | } catch { 75 | print("❌ Restart method failed: \(error)") 76 | } 77 | 78 | // Check final status 79 | usleep(1000000) 80 | let finalStatus = SystemOSDManager.isOSDUIHelperRunning() 81 | print("📊 Final OSDUIHelper status: \(finalStatus ? "✅ Running" : "❌ Not running")") 82 | } 83 | } -------------------------------------------------------------------------------- /DynamicIsland/managers/ClipboardPanelManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClipboardPanelManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by Ebullioscopic on 12/08/25. 6 | // 7 | 8 | import AppKit 9 | import SwiftUI 10 | 11 | class ClipboardPanelManager: ObservableObject { 12 | static let shared = ClipboardPanelManager() 13 | 14 | private var clipboardPanel: ClipboardPanel? 15 | 16 | private init() {} 17 | 18 | func showClipboardPanel() { 19 | hideClipboardPanel() // Close any existing panel 20 | 21 | let panel = ClipboardPanel() 22 | panel.positionNearNotch() 23 | 24 | self.clipboardPanel = panel 25 | 26 | // Make the panel key and order front to ensure it can receive focus 27 | panel.makeKeyAndOrderFront(nil) 28 | panel.orderFrontRegardless() 29 | 30 | // Activate the app to ensure proper focus handling 31 | NSApp.activate(ignoringOtherApps: true) 32 | 33 | // Ensure the panel becomes the key window for text input 34 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 35 | panel.makeKey() 36 | } 37 | } 38 | 39 | func hideClipboardPanel() { 40 | clipboardPanel?.close() 41 | clipboardPanel = nil 42 | } 43 | 44 | func toggleClipboardPanel() { 45 | if let panel = clipboardPanel, panel.isVisible { 46 | hideClipboardPanel() 47 | } else { 48 | showClipboardPanel() 49 | } 50 | } 51 | 52 | var isPanelVisible: Bool { 53 | return clipboardPanel?.isVisible ?? false 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DynamicIsland/managers/ClipboardWindowManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClipboardWindowManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by Ebullioscopic on 12/08/25. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | 11 | class ClipboardWindowManager: ObservableObject { 12 | static let shared = ClipboardWindowManager() 13 | 14 | private var clipboardWindow: NSWindow? 15 | 16 | private init() {} 17 | 18 | func showClipboardWindow() { 19 | if let existingWindow = clipboardWindow { 20 | // Ensure window appears above fullscreen apps 21 | existingWindow.level = .screenSaver 22 | existingWindow.makeKeyAndOrderFront(nil) 23 | existingWindow.orderFrontRegardless() // Force window to front even above fullscreen 24 | NSApp.activate(ignoringOtherApps: true) 25 | return 26 | } 27 | 28 | // Create the window 29 | let window = NSWindow( 30 | contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), 31 | styleMask: [.titled, .closable, .resizable], 32 | backing: .buffered, 33 | defer: false 34 | ) 35 | 36 | window.title = "Clipboard Manager" 37 | window.isMovableByWindowBackground = true 38 | window.level = .screenSaver // Use screenSaver level to appear above fullscreen apps 39 | window.isReleasedWhenClosed = false 40 | window.hasShadow = true 41 | window.titlebarAppearsTransparent = true 42 | window.styleMask.insert(.fullSizeContentView) 43 | window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] // Allow on all spaces and above fullscreen 44 | 45 | // Set minimum and maximum sizes 46 | window.minSize = NSSize(width: 350, height: 250) 47 | window.maxSize = NSSize(width: 600, height: 500) 48 | 49 | // Center the window on the current screen (important for fullscreen apps) 50 | let currentScreen = NSScreen.main ?? NSScreen.screens.first! 51 | let screenFrame = currentScreen.frame // Use full frame instead of visibleFrame for fullscreen compatibility 52 | let windowFrame = window.frame 53 | let x = (screenFrame.width - windowFrame.width) / 2 + screenFrame.minX 54 | let y = (screenFrame.height - windowFrame.height) / 2 + screenFrame.minY 55 | window.setFrameOrigin(NSPoint(x: x, y: y)) 56 | 57 | // Set the content view 58 | let contentView = ClipboardWindow() 59 | let hostingView = NSHostingView(rootView: contentView) 60 | window.contentView = hostingView 61 | 62 | // Handle window closing 63 | window.delegate = WindowDelegate { [weak self] in 64 | self?.clipboardWindow = nil 65 | } 66 | 67 | self.clipboardWindow = window 68 | window.makeKeyAndOrderFront(nil) 69 | window.orderFrontRegardless() // Force window to front even above fullscreen apps 70 | NSApp.activate(ignoringOtherApps: true) 71 | } 72 | 73 | func hideClipboardWindow() { 74 | clipboardWindow?.close() 75 | } 76 | 77 | func toggleClipboardWindow() { 78 | if let window = clipboardWindow, window.isVisible { 79 | hideClipboardWindow() 80 | } else { 81 | showClipboardWindow() 82 | } 83 | } 84 | 85 | var isWindowVisible: Bool { 86 | return clipboardWindow?.isVisible ?? false 87 | } 88 | } 89 | 90 | private class WindowDelegate: NSObject, NSWindowDelegate { 91 | private let onClose: () -> Void 92 | 93 | init(onClose: @escaping () -> Void) { 94 | self.onClose = onClose 95 | super.init() 96 | } 97 | 98 | func windowWillClose(_ notification: Notification) { 99 | onClose() 100 | } 101 | 102 | func windowShouldClose(_ sender: NSWindow) -> Bool { 103 | // Hide instead of close when user clicks close button 104 | sender.orderOut(nil) 105 | onClose() 106 | return false 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /DynamicIsland/managers/ColorPickerManager_Fixed.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIsland/managers/ColorPickerManager_Fixed.swift -------------------------------------------------------------------------------- /DynamicIsland/managers/DynamicIslandExtensionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicIslandExtensionManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // DynamicIslandExtensionManager.swift 7 | // DynamicIsland 8 | // 9 | // Created by Harsh Vardhan Goswami on 07/09/24. 10 | // 11 | 12 | import Foundation 13 | import SwiftUI 14 | 15 | var clipboardExtension: String = "dynamicisland.TheDynamicClipboard" 16 | var hudExtension: String = "dynamicisland.TheDynamicHUDs" 17 | var downloadManagerExtension: String = "dynamicisland.TheDynamicDownloadManager" 18 | 19 | struct Extension: Identifiable, Hashable { 20 | var id = UUID() 21 | var name: String 22 | var bundleIdentifier: String 23 | var status: StatusModel = .enabled 24 | } 25 | 26 | enum StatusModel { 27 | case disabled 28 | case enabled 29 | } 30 | 31 | class DynamicIslandExtensionManager: ObservableObject { 32 | 33 | @Published var installedExtensions: [Extension] = [] { 34 | didSet { 35 | // Only log when there's an actual change in content, not just initialization 36 | if oldValue != installedExtensions { 37 | print("📦 Extensions installed: \(installedExtensions.map { $0.name })") 38 | } 39 | } 40 | } 41 | 42 | var extensions = [ 43 | clipboardExtension, 44 | hudExtension 45 | ] 46 | 47 | init() { 48 | checkIfExtensionsAreInstalled() 49 | 50 | DistributedNotificationCenter.default().addObserver(self, selector: #selector(checkIfExtensionsAreInstalled), name: NSNotification.Name("NSWorkspaceDidLaunchApplicationNotification"), object: nil) 51 | } 52 | 53 | @objc func checkIfExtensionsAreInstalled() { 54 | installedExtensions = [] 55 | for extensionName in extensions { 56 | if NSWorkspace.shared.urlForApplication(withBundleIdentifier: extensionName) != nil { 57 | let ext = Extension(name: extensionName.components(separatedBy: ".").last ?? extensionName, bundleIdentifier: extensionName) 58 | installedExtensions.append(ext) 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DynamicIsland/managers/ImageService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageService.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2025-09-13. 6 | // 7 | 8 | import Foundation 9 | import Defaults 10 | 11 | public protocol ImageServiceProtocol { 12 | func fetchImageData(from url: URL) async throws -> Data 13 | } 14 | 15 | public final class ImageService: ImageServiceProtocol { 16 | public static let shared = ImageService() 17 | 18 | private let session: URLSession 19 | 20 | private init() { 21 | let config = URLSessionConfiguration.default 22 | let cache = URLCache(memoryCapacity: 50 * 1024 * 1024, // 50MB 23 | diskCapacity: 100 * 1024 * 1024, // 100MB 24 | diskPath: "artwork_cache") 25 | config.urlCache = cache 26 | config.timeoutIntervalForRequest = 15 27 | config.timeoutIntervalForResource = 30 28 | config.httpShouldSetCookies = false 29 | self.session = URLSession(configuration: config) 30 | 31 | performLegacyCacheCleanupIfNeeded() 32 | } 33 | 34 | private func performLegacyCacheCleanupIfNeeded() { 35 | 36 | if !Defaults[.didClearLegacyURLCacheV1] { 37 | URLCache.shared.removeAllCachedResponses() 38 | Defaults[.didClearLegacyURLCacheV1] = true 39 | } 40 | } 41 | 42 | public func fetchImageData(from url: URL) async throws -> Data { 43 | guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else { 44 | throw URLError(.unsupportedURL) 45 | } 46 | let (data, _) = try await session.data(from: url) 47 | return data 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DynamicIsland/managers/NotchSpaceManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotchSpaceManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander on 2024-10-27. 6 | // 7 | 8 | import Foundation 9 | 10 | class NotchSpaceManager { 11 | static let shared = NotchSpaceManager() 12 | let notchSpace: CGSSpace 13 | private var eventTap: CFMachPort? 14 | private var runLoopSource: CFRunLoopSource? 15 | 16 | private init() { 17 | notchSpace = CGSSpace(level: 2147483647) // Max level 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DynamicIsland/managers/ScreenAssistantPanelManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenAssistantPanelManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by Hariharan Mudaliar 6 | 7 | import AppKit 8 | import SwiftUI 9 | import Defaults 10 | 11 | class ScreenAssistantPanelManager: ObservableObject { 12 | static let shared = ScreenAssistantPanelManager() 13 | 14 | // Backward compatibility wrapper - delegates to the new ScreenAssistantManager 15 | private var screenAssistantPanel: ScreenAssistantPanel? 16 | 17 | private init() {} 18 | 19 | func showScreenAssistantPanel() { 20 | hideScreenAssistantPanel() // Close any existing panel 21 | 22 | // Use the new dual-panel system 23 | ScreenAssistantManager.shared.showPanels() 24 | 25 | // Create a dummy panel for backward compatibility 26 | let panel = ScreenAssistantPanel() 27 | self.screenAssistantPanel = panel 28 | 29 | // The actual panels are managed by ScreenAssistantManager 30 | // This is just for compatibility with existing code 31 | 32 | print("ScreenAssistant: New dual-panel system activated") 33 | } 34 | 35 | func hideScreenAssistantPanel() { 36 | // Close the new panel system 37 | ScreenAssistantManager.shared.closePanels() 38 | 39 | // Clean up compatibility panel 40 | screenAssistantPanel?.close() 41 | screenAssistantPanel = nil 42 | 43 | print("ScreenAssistant: Panels hidden") 44 | } 45 | 46 | func toggleScreenAssistantPanel() { 47 | if ScreenAssistantManager.shared.arePanelsVisible() { 48 | hideScreenAssistantPanel() 49 | } else { 50 | showScreenAssistantPanel() 51 | } 52 | } 53 | 54 | var isPanelVisible: Bool { 55 | return ScreenAssistantManager.shared.arePanelsVisible() 56 | } 57 | } -------------------------------------------------------------------------------- /DynamicIsland/managers/StatsPanelManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatsPanelManager.swift 3 | // DynamicIsland 4 | // 5 | // Created by DynamicIsland App on 2024. 6 | // 7 | 8 | import AppKit 9 | import SwiftUI 10 | 11 | class StatsPanelManager: ObservableObject { 12 | static let shared = StatsPanelManager() 13 | 14 | private var statsPanel: StatsPanel? 15 | 16 | private init() {} 17 | 18 | func showStatsPanel() { 19 | hideStatsPanel() // Close any existing panel 20 | 21 | let panel = StatsPanel() 22 | panel.positionNearNotch() 23 | 24 | self.statsPanel = panel 25 | 26 | // Make the panel key and order front to ensure it can receive focus 27 | panel.makeKeyAndOrderFront(nil) 28 | panel.orderFrontRegardless() 29 | 30 | // Activate the app to ensure proper focus handling 31 | NSApp.activate(ignoringOtherApps: true) 32 | 33 | // Ensure the panel becomes the key window for input 34 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 35 | panel.makeKey() 36 | } 37 | } 38 | 39 | func hideStatsPanel() { 40 | statsPanel?.close() 41 | statsPanel = nil 42 | } 43 | 44 | func toggleStatsPanel() { 45 | if let panel = statsPanel, panel.isVisible { 46 | hideStatsPanel() 47 | } else { 48 | showStatsPanel() 49 | } 50 | } 51 | 52 | var isPanelVisible: Bool { 53 | return statsPanel?.isVisible ?? false 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DynamicIsland/managers/SystemDisplayManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemDisplayManager.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD DisplayManager.swift 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | class SystemDisplayManager { 13 | private init() {} 14 | 15 | private static var method = SensorMethod.standard 16 | 17 | static func getDisplayBrightness() throws -> Float { 18 | switch SystemDisplayManager.method { 19 | case .standard: 20 | do { 21 | return try getStandardDisplayBrightness() 22 | } catch { 23 | method = .m1 24 | } 25 | case .m1: 26 | do { 27 | return try getM1DisplayBrightness() 28 | } catch { 29 | method = .allFailed 30 | } 31 | case .allFailed: 32 | throw SensorError.Display.notFound 33 | } 34 | return try getDisplayBrightness() 35 | } 36 | 37 | private static func getStandardDisplayBrightness() throws -> Float { 38 | var brightness: float_t = 1 39 | let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect")) 40 | defer { 41 | IOObjectRelease(service) 42 | } 43 | 44 | let result = IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &brightness) 45 | if result != kIOReturnSuccess { 46 | throw SensorError.Display.notStandard 47 | } 48 | return brightness 49 | } 50 | 51 | private static func getM1DisplayBrightness() throws -> Float { 52 | let task = Process() 53 | task.launchPath = "/usr/libexec/corebrightnessdiag" 54 | task.arguments = ["status-info"] 55 | let pipe = Pipe() 56 | task.standardOutput = pipe 57 | try task.run() 58 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 59 | task.waitUntilExit() 60 | 61 | if let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? NSDictionary, 62 | let displays = plist["CBDisplays"] as? [String: [String: Any]] { 63 | for display in displays.values { 64 | if let displayInfo = display["Display"] as? [String: Any], 65 | displayInfo["DisplayServicesIsBuiltInDisplay"] as? Bool == true, 66 | let brightness = displayInfo["DisplayServicesBrightness"] as? Float { 67 | return brightness 68 | } 69 | } 70 | } 71 | throw SensorError.Display.notSilicon 72 | } 73 | } -------------------------------------------------------------------------------- /DynamicIsland/managers/SystemKeyObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemKeyObserver.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD KeyPressObserver.swift 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | // ⚠️ DEPRECATED: This class is non-functional as it extends NSApplication 9 | // but is never used as the main app class. Event monitoring has been moved 10 | // to SystemChangesObserver using NSEvent.addGlobalMonitorForEvents. 11 | // 12 | 13 | import Cocoa 14 | 15 | class SystemKeyObserver: NSApplication { 16 | static let volumeChanged = Notification.Name("DynamicIsland.volumeChanged") 17 | static let brightnessChanged = Notification.Name("DynamicIsland.brightnessChanged") 18 | static let keyboardIlluminationChanged = Notification.Name("DynamicIsland.keyboardIlluminationChanged") 19 | 20 | override func sendEvent(_ event: NSEvent) { 21 | if event.type == .systemDefined && event.subtype.rawValue == 8 { 22 | let keyCode = ((event.data1 & 0xFFFF0000) >> 16) 23 | let keyFlags = (event.data1 & 0x0000FFFF) 24 | // Get the key state. 0xA is KeyDown, OxB is KeyUp 25 | let keyState = ((keyFlags & 0xFF00) >> 8) == 0xA 26 | let keyRepeat = keyFlags & 0x1 27 | mediaKeyEvent(key: Int32(keyCode), state: keyState, keyRepeat: Bool(truncating: keyRepeat as NSNumber)) 28 | } 29 | 30 | super.sendEvent(event) 31 | } 32 | 33 | func mediaKeyEvent(key: Int32, state: Bool, keyRepeat: Bool) { 34 | // Only send events on KeyDown. Without this check, these events will happen twice 35 | if state { 36 | switch key { 37 | case NX_KEYTYPE_SOUND_DOWN, NX_KEYTYPE_SOUND_UP, NX_KEYTYPE_MUTE: 38 | NotificationCenter.default.post(name: SystemKeyObserver.volumeChanged, object: self) 39 | case NX_KEYTYPE_BRIGHTNESS_UP, NX_KEYTYPE_BRIGHTNESS_DOWN: 40 | NotificationCenter.default.post(name: SystemKeyObserver.brightnessChanged, object: self) 41 | case NX_KEYTYPE_ILLUMINATION_DOWN, NX_KEYTYPE_ILLUMINATION_UP: 42 | NotificationCenter.default.post(name: SystemKeyObserver.keyboardIlluminationChanged, object: self) 43 | default: 44 | break 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /DynamicIsland/managers/SystemVolumeManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemVolumeManager.swift 3 | // DynamicIsland 4 | // 5 | // Adapted from TheBoringWorker-HUD VolumeManager.swift 6 | // Created by GitHub Copilot on 06/09/25. 7 | // 8 | 9 | import Foundation 10 | 11 | class SystemVolumeManager { 12 | private init() {} 13 | 14 | static func isMuted() -> Bool { 15 | do { 16 | return try AppleScriptRunner.run(script: "return output muted of (get volume settings)") == "true" 17 | } catch { 18 | NSLog("Error while trying to retrieve muted properties of device: \(error). Returning default value false.") 19 | return false 20 | } 21 | } 22 | 23 | static func getOutputVolume() -> Float { 24 | do { 25 | if let volumeStr = Float(try AppleScriptRunner.run(script: "return output volume of (get volume settings)")) { 26 | return volumeStr / 100 27 | } else { 28 | NSLog("Error while trying to parse volume string value. Returning default volume value 1.") 29 | } 30 | } catch { 31 | NSLog("Error while trying to retrieve volume properties of device: \(error). Returning default volume value 1.") 32 | } 33 | return 0.01 34 | } 35 | } -------------------------------------------------------------------------------- /DynamicIsland/menu/StatusBarMenu.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class StatusBarMenu: NSMenu { 4 | 5 | var statusItem: NSStatusItem! 6 | 7 | override init(title: String) { 8 | super.init(title: title) 9 | setupStatusItem() 10 | } 11 | 12 | convenience init() { 13 | self.init(title: "DynamicIsland") 14 | } 15 | 16 | required init(coder: NSCoder) { 17 | super.init(coder: coder) 18 | setupStatusItem() 19 | } 20 | 21 | private func setupStatusItem() { 22 | // Initialize the status item 23 | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) 24 | 25 | // Set the menu bar icon 26 | if let button = statusItem.button { 27 | button.image = NSImage(named: "logo") 28 | } 29 | 30 | // Set up the menu 31 | self.addItem(NSMenuItem(title: "Quit", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q")) 32 | statusItem.menu = self 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /DynamicIsland/metal/visualizer.metal: -------------------------------------------------------------------------------- 1 | // 2 | // visualizer.metal 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 28/08/24. 6 | // 7 | 8 | #include 9 | using namespace metal; 10 | 11 | vertex float4 vertexShader(uint vertexID [[vertex_id]], 12 | constant float2 *vertices [[buffer(0)]]) { 13 | return float4(vertices[vertexID], 0, 1); 14 | } 15 | 16 | fragment float4 fragmentShader(constant float4 &color [[buffer(0)]]) { 17 | return color; 18 | } 19 | -------------------------------------------------------------------------------- /DynamicIsland/models/CalendarModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalendarModel.swift 3 | // Calendr 4 | // 5 | // Created by Paker on 31/12/20. 6 | // Original source: https://github.com/pakerwreah/Calendr 7 | // 8 | 9 | import Cocoa 10 | 11 | struct CalendarModel: Identifiable, Hashable { 12 | let id: String 13 | let title: String 14 | let color: NSColor 15 | let isSubscribed: Bool 16 | let isReminder: Bool // true if this is a reminder calendar 17 | } 18 | -------------------------------------------------------------------------------- /DynamicIsland/models/EventModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventModel.swift 3 | // Calendr 4 | // 5 | // Created by Paker on 24/12/20. 6 | // Original source: https://github.com/pakerwreah/Calendr 7 | // Modified by Alexander on 2025-05-18. 8 | // 9 | 10 | import Foundation 11 | 12 | struct EventModel: Equatable, Identifiable { 13 | let id: String 14 | let start: Date 15 | let end: Date 16 | let title: String 17 | let location: String? 18 | let notes: String? 19 | let url: URL? 20 | let isAllDay: Bool 21 | let type: EventType 22 | let calendar: CalendarModel 23 | let participants: [Participant] 24 | let timeZone: TimeZone? 25 | let hasRecurrenceRules: Bool 26 | let priority: Priority? 27 | } 28 | 29 | enum AttendanceStatus: Comparable { 30 | case accepted 31 | case maybe 32 | case pending 33 | case declined 34 | case unknown 35 | 36 | private var comparisonValue: Int { 37 | switch self { 38 | case .accepted: return 1 39 | case .maybe: return 2 40 | case .declined: return 3 41 | case .pending: return 4 42 | case .unknown: return 5 43 | } 44 | } 45 | 46 | static func < (lhs: Self, rhs: Self) -> Bool { 47 | return lhs.comparisonValue < rhs.comparisonValue 48 | } 49 | } 50 | 51 | enum EventType: Equatable { 52 | case event(AttendanceStatus) 53 | case birthday 54 | case reminder(completed: Bool) 55 | } 56 | 57 | enum EventStatus: Equatable { 58 | case upcoming 59 | case inProgress 60 | case ended 61 | } 62 | 63 | extension EventType { 64 | var isEvent: Bool { if case .event = self { return true } else { return false } } 65 | var isBirthday: Bool { self ~= .birthday } 66 | var isReminder: Bool { if case .reminder = self { return true } else { return false } } 67 | } 68 | 69 | extension EventModel { 70 | 71 | var eventStatus: EventStatus { 72 | if start > Date() { 73 | return .upcoming 74 | } else if end > Date() { 75 | return .inProgress 76 | } else { 77 | return .ended 78 | } 79 | } 80 | 81 | var attendance: AttendanceStatus { if case .event(let attendance) = type { return attendance } else { return .unknown } } 82 | 83 | var isMeeting: Bool { !participants.isEmpty } 84 | 85 | func calendarAppURL() -> URL? { 86 | 87 | guard let id = id.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { 88 | return nil 89 | } 90 | 91 | guard !type.isReminder else { 92 | return URL(string: "x-apple-reminderkit://remcdreminder/\(id)") 93 | } 94 | 95 | let date: String 96 | if hasRecurrenceRules { 97 | let formatter = DateFormatter() 98 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" 99 | if !isAllDay { 100 | formatter.timeZone = .init(secondsFromGMT: 0) 101 | } 102 | if let formattedDate = formatter.string(for: start) { 103 | date = "/\(formattedDate)" 104 | } else { 105 | return nil 106 | } 107 | } else { 108 | date = "" 109 | } 110 | return URL(string: "ical://ekevent\(date)/\(id)?method=show&options=more") 111 | } 112 | } 113 | 114 | struct Participant: Hashable { 115 | let name: String 116 | let status: AttendanceStatus 117 | let isOrganizer: Bool 118 | let isCurrentUser: Bool 119 | } 120 | 121 | enum Priority { 122 | case high 123 | case medium 124 | case low 125 | } 126 | -------------------------------------------------------------------------------- /DynamicIsland/models/PlaybackState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaybackState.swift 3 | // DynamicIsland 4 | // 5 | // Created by Alexander Greco on 2025-03-29. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RepeatMode: Int, Codable { 11 | case off = 1 12 | case one = 2 13 | case all = 3 14 | } 15 | 16 | struct PlaybackState { 17 | var bundleIdentifier: String 18 | var isPlaying: Bool = false 19 | var title: String = "I'm Handsome" 20 | var artist: String = "Me" 21 | var album: String = "Self Love" 22 | var currentTime: Double = 0 23 | var duration: Double = 0 24 | var playbackRate: Double = 1 25 | var isShuffled: Bool = false 26 | var repeatMode: RepeatMode = .off 27 | var lastUpdated: Date = Date.distantPast 28 | var artwork: Data? 29 | } 30 | 31 | extension PlaybackState: Equatable { 32 | static func == (lhs: PlaybackState, rhs: PlaybackState) -> Bool { 33 | return lhs.bundleIdentifier == rhs.bundleIdentifier 34 | && lhs.isPlaying == rhs.isPlaying 35 | && lhs.title == rhs.title 36 | && lhs.artist == rhs.artist 37 | && lhs.album == rhs.album 38 | && lhs.currentTime == rhs.currentTime 39 | && lhs.duration == rhs.duration 40 | && lhs.isShuffled == rhs.isShuffled 41 | && lhs.repeatMode == rhs.repeatMode 42 | && lhs.artwork == rhs.artwork 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DynamicIsland/observers/FullscreenMediaDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FullscreenMediaDetection.swift 3 | // DynamicIsland 4 | // 5 | // Created by Richard Kunkli on 06/09/2024. 6 | // 7 | 8 | import Defaults 9 | import MacroVisionKit 10 | import SwiftUI 11 | 12 | class FullscreenMediaDetector: ObservableObject { 13 | static let shared = FullscreenMediaDetector() 14 | private let detector: MacroVisionKit 15 | @ObservedObject private var musicManager = MusicManager.shared 16 | @MainActor @Published private(set) var fullscreenStatus: [String: Bool] = [:] 17 | private var notificationTask: Task? 18 | 19 | private init() { 20 | self.detector = MacroVisionKit.shared 21 | detector.configuration.includeSystemApps = true 22 | setupNotificationObservers() 23 | updateFullScreenStatus() 24 | } 25 | 26 | private func setupNotificationObservers() { 27 | notificationTask = Task { @Sendable [weak self] in 28 | await withTaskGroup(of: Void.self) { group in 29 | group.addTask { 30 | let activeSpaceNotifications = NSWorkspace.shared.notificationCenter.notifications( 31 | named: NSWorkspace.activeSpaceDidChangeNotification 32 | ) 33 | 34 | for await _ in activeSpaceNotifications { 35 | await self?.handleChange() 36 | } 37 | } 38 | 39 | group.addTask { 40 | let screenParameterNotifications = NSWorkspace.shared.notificationCenter.notifications( 41 | named: NSApplication.didChangeScreenParametersNotification 42 | ) 43 | 44 | for await _ in screenParameterNotifications { 45 | await self?.handleChange() 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | private func handleChange() async { 53 | try? await Task.sleep(for: .milliseconds(500)) 54 | self.updateFullScreenStatus() 55 | } 56 | 57 | private func updateFullScreenStatus() { 58 | guard Defaults[.enableFullscreenMediaDetection] else { 59 | let reset = Dictionary(uniqueKeysWithValues: NSScreen.screens.map { ($0.localizedName, false) }) 60 | if reset != fullscreenStatus { 61 | fullscreenStatus = reset 62 | } 63 | return 64 | } 65 | 66 | 67 | let apps = detector.detectFullscreenApps(debug: false) 68 | let names = NSScreen.screens.map { $0.localizedName } 69 | var newStatus: [String: Bool] = [:] 70 | for name in names { 71 | newStatus[name] = apps.contains { $0.screen.localizedName == name && $0.bundleIdentifier != "com.apple.finder" && ($0.bundleIdentifier == musicManager.bundleIdentifier || Defaults[.hideNotchOption] == .always) } 72 | } 73 | 74 | if newStatus != fullscreenStatus { 75 | fullscreenStatus = newStatus 76 | NSLog("✅ Fullscreen status: \(newStatus)") 77 | } 78 | } 79 | 80 | private func cleanupNotificationObservers() { 81 | NSWorkspace.shared.notificationCenter.removeObserver(self) 82 | } 83 | 84 | deinit { 85 | NSWorkspace.shared.notificationCenter.removeObserver(self) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /DynamicIsland/private/CGSSpace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGSSpace.swift 3 | // DynamicIsland 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this 7 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | // 9 | // Original source: https://github.com/avaidyam/Parrot/ 10 | // Modified by Alexander Greco on 2024-10-27 11 | 12 | import AppKit 13 | 14 | /// Small Spaces API wrapper. 15 | public final class CGSSpace { 16 | private let identifier: CGSSpaceID 17 | private let createdByInit: Bool 18 | 19 | public var windows: Set = [] { 20 | didSet { 21 | let remove = oldValue.subtracting(self.windows) 22 | let add = self.windows.subtracting(oldValue) 23 | 24 | CGSRemoveWindowsFromSpaces(_CGSDefaultConnection(), 25 | remove.map { $0.windowNumber } as NSArray, 26 | [self.identifier]) 27 | CGSAddWindowsToSpaces(_CGSDefaultConnection(), 28 | add.map { $0.windowNumber } as NSArray, 29 | [self.identifier]) 30 | } 31 | } 32 | 33 | /// Initialized `CGSSpace`s *MUST* be de-initialized upon app exit! 34 | public init(level: Int = 0) { 35 | let flag = 0x1 // this value MUST be 1, otherwise, Finder decides to draw desktop icons 36 | self.identifier = CGSSpaceCreate(_CGSDefaultConnection(), flag, nil) 37 | CGSSpaceSetAbsoluteLevel(_CGSDefaultConnection(), self.identifier, level) 38 | CGSShowSpaces(_CGSDefaultConnection(), [self.identifier]) 39 | self.createdByInit = true // Mark as created by the first init 40 | } 41 | 42 | public init(id: UInt64) { 43 | let flag = 0x1 // this value MUST be 1, otherwise, Finder decides to draw desktop icons 44 | self.identifier = id 45 | CGSShowSpaces(_CGSDefaultConnection(), [self.identifier]) 46 | self.createdByInit = false // Mark as created externally 47 | } 48 | 49 | deinit { 50 | CGSHideSpaces(_CGSDefaultConnection(), [self.identifier]) 51 | // Only call CGSSpaceDestroy if the space was created by the first init 52 | if createdByInit { 53 | CGSSpaceDestroy(_CGSDefaultConnection(), self.identifier) 54 | } 55 | } 56 | } 57 | 58 | // CGSSpace stuff: 59 | fileprivate typealias CGSConnectionID = UInt 60 | fileprivate typealias CGSSpaceID = UInt64 61 | @_silgen_name("_CGSDefaultConnection") 62 | fileprivate func _CGSDefaultConnection() -> CGSConnectionID 63 | @_silgen_name("CGSSpaceCreate") 64 | fileprivate func CGSSpaceCreate(_ cid: CGSConnectionID, _ unknown: Int, _ options: NSDictionary?) -> CGSSpaceID 65 | @_silgen_name("CGSSpaceDestroy") 66 | fileprivate func CGSSpaceDestroy(_ cid: CGSConnectionID, _ space: CGSSpaceID) 67 | @_silgen_name("CGSSpaceSetAbsoluteLevel") 68 | fileprivate func CGSSpaceSetAbsoluteLevel(_ cid: CGSConnectionID, _ space: CGSSpaceID, _ level: Int) 69 | @_silgen_name("CGSAddWindowsToSpaces") 70 | fileprivate func CGSAddWindowsToSpaces(_ cid: CGSConnectionID, _ windows: NSArray, _ spaces: NSArray) 71 | @_silgen_name("CGSRemoveWindowsFromSpaces") 72 | fileprivate func CGSRemoveWindowsFromSpaces(_ cid: CGSConnectionID, _ windows: NSArray, _ spaces: NSArray) 73 | @_silgen_name("CGSHideSpaces") 74 | fileprivate func CGSHideSpaces(_ cid: CGSConnectionID, _ spaces: NSArray) 75 | @_silgen_name("CGSShowSpaces") 76 | fileprivate func CGSShowSpaces(_ cid: CGSConnectionID, _ spaces: NSArray) 77 | -------------------------------------------------------------------------------- /DynamicIsland/sizing/matters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // matters.swift 4 | // DynamicIsland 5 | // 6 | // 7 | // Created by Harsh Vardhan Goswami on 05/08/24. 8 | // 9 | 10 | import Defaults 11 | import Foundation 12 | import SwiftUI 13 | 14 | let downloadSneakSize: CGSize = .init(width: 65, height: 1) 15 | let batterySneakSize: CGSize = .init(width: 160, height: 1) 16 | 17 | let openNotchSize: CGSize = .init(width: 640, height: 190) 18 | let minimalisticOpenNotchSize: CGSize = .init(width: 420, height: 180) 19 | let cornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 19, bottom: 24), closed: (top: 6, bottom: 14)) 20 | let minimalisticCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 35, bottom: 35), closed: cornerRadiusInsets.closed) 21 | 22 | enum MusicPlayerImageSizes { 23 | static let cornerRadiusInset: (opened: CGFloat, closed: CGFloat) = (opened: 13.0, closed: 4.0) 24 | static let size = (opened: CGSize(width: 90, height: 90), closed: CGSize(width: 20, height: 20)) 25 | } 26 | 27 | func getScreenFrame(_ screen: String? = nil) -> CGRect? { 28 | var selectedScreen = NSScreen.main 29 | 30 | if let customScreen = screen { 31 | selectedScreen = NSScreen.screens.first(where: { $0.localizedName == customScreen }) 32 | } 33 | 34 | if let screen = selectedScreen { 35 | return screen.frame 36 | } 37 | 38 | return nil 39 | } 40 | 41 | func getClosedNotchSize(screen: String? = nil) -> CGSize { 42 | // Default notch size, to avoid using optionals 43 | var notchHeight: CGFloat = Defaults[.nonNotchHeight] 44 | var notchWidth: CGFloat = 185 45 | 46 | var selectedScreen = NSScreen.main 47 | 48 | if let customScreen = screen { 49 | selectedScreen = NSScreen.screens.first(where: { $0.localizedName == customScreen }) 50 | } 51 | 52 | // Check if the screen is available 53 | if let screen = selectedScreen { 54 | // Calculate and set the exact width of the notch 55 | if let topLeftNotchpadding: CGFloat = screen.auxiliaryTopLeftArea?.width, 56 | let topRightNotchpadding: CGFloat = screen.auxiliaryTopRightArea?.width 57 | { 58 | notchWidth = screen.frame.width - topLeftNotchpadding - topRightNotchpadding + 4 59 | } 60 | 61 | // Check if the Mac has a notch 62 | if screen.safeAreaInsets.top > 0 { 63 | // This is a display WITH a notch - use notch height settings 64 | notchHeight = Defaults[.notchHeight] 65 | if Defaults[.notchHeightMode] == .matchRealNotchSize { 66 | notchHeight = screen.safeAreaInsets.top 67 | } else if Defaults[.notchHeightMode] == .matchMenuBar { 68 | notchHeight = screen.frame.maxY - screen.visibleFrame.maxY 69 | } 70 | } else { 71 | // This is a display WITHOUT a notch - use non-notch height settings 72 | notchHeight = Defaults[.nonNotchHeight] 73 | if Defaults[.nonNotchHeightMode] == .matchMenuBar { 74 | notchHeight = screen.frame.maxY - screen.visibleFrame.maxY 75 | } 76 | } 77 | } 78 | 79 | return .init(width: notchWidth, height: notchHeight) 80 | } 81 | -------------------------------------------------------------------------------- /DynamicIsland/strings/constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // constants.swift 3 | // DynamicIsland 4 | // 5 | // Created by Harsh Vardhan Goswami on 04/08/24. 6 | // 7 | 8 | import Foundation 9 | 10 | let productPage = URL(string: "https://github.com/Ebullioscopic/DynamicIsland")! 11 | let sponsorPage = URL(string: "https://www.linkedin.com/in/hariharan-mudaliar/")! 12 | 13 | 14 | -------------------------------------------------------------------------------- /DynamicIsland/utils/Logger.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | enum LogCategory: String { 5 | case lifecycle = "🔄" 6 | case memory = "💾" 7 | case performance = "⚡️" 8 | case ui = "🎨" 9 | case network = "🌐" 10 | case error = "❌" 11 | case warning = "⚠️" 12 | case success = "✅" 13 | case debug = "🔍" 14 | } 15 | 16 | struct Logger { 17 | static func log( 18 | _ message: String, 19 | category: LogCategory, 20 | file: String = #file, 21 | function: String = #function, 22 | line: Int = #line 23 | ) { 24 | let fileName = (file as NSString).lastPathComponent 25 | let timestamp = ISO8601DateFormatter().string(from: Date()) 26 | print("\(category.rawValue) [\(timestamp)] [\(fileName):\(line)] \(function) - \(message)") 27 | } 28 | 29 | static func trackMemory( 30 | file: String = #file, 31 | function: String = #function, 32 | line: Int = #line 33 | ) { 34 | var info = mach_task_basic_info() 35 | var count = mach_msg_type_number_t(MemoryLayout.size)/4 36 | 37 | let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) { 38 | $0.withMemoryRebound(to: integer_t.self, capacity: 1) { 39 | task_info(mach_task_self_, 40 | task_flavor_t(MACH_TASK_BASIC_INFO), 41 | $0, 42 | &count) 43 | } 44 | } 45 | 46 | if kerr == KERN_SUCCESS { 47 | let usedMB = Double(info.resident_size) / 1024.0 / 1024.0 48 | log(String(format: "Memory used: %.2f MB", usedMB), 49 | category: .memory, 50 | file: file, 51 | function: function, 52 | line: line) 53 | } 54 | } 55 | } 56 | 57 | extension View { 58 | func trackLifecycle(_ identifier: String) -> some View { 59 | self.modifier(ViewLifecycleTracker(identifier: identifier)) 60 | } 61 | } 62 | 63 | struct ViewLifecycleTracker: ViewModifier { 64 | let identifier: String 65 | 66 | func body(content: Content) -> some View { 67 | content 68 | .onAppear { 69 | Logger.log("\(identifier) appeared", category: .lifecycle) 70 | Logger.trackMemory() 71 | } 72 | .onDisappear { 73 | Logger.log("\(identifier) disappeared", category: .lifecycle) 74 | Logger.trackMemory() 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /DynamicIslandSamples/clipboardpanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/clipboardpanel.png -------------------------------------------------------------------------------- /DynamicIslandSamples/clipboardpopover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/clipboardpopover.png -------------------------------------------------------------------------------- /DynamicIslandSamples/colorpickerpanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/colorpickerpanel.png -------------------------------------------------------------------------------- /DynamicIslandSamples/colorpickerpopover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/colorpickerpopover.png -------------------------------------------------------------------------------- /DynamicIslandSamples/dynamicisland-minimalistic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/dynamicisland-minimalistic.png -------------------------------------------------------------------------------- /DynamicIslandSamples/dynamicislandscreenrecord.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/dynamicislandscreenrecord.gif -------------------------------------------------------------------------------- /DynamicIslandSamples/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/logo.png -------------------------------------------------------------------------------- /DynamicIslandSamples/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/media.png -------------------------------------------------------------------------------- /DynamicIslandSamples/statsmonitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/DynamicIslandSamples/statsmonitor.png -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/MediaRemoteAdapter: -------------------------------------------------------------------------------- 1 | Versions/Current/MediaRemoteAdapter -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/Versions/A/MediaRemoteAdapter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/Frameworks/MediaRemoteAdapter.framework/Versions/A/MediaRemoteAdapter -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | MediaRemoteAdapter 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.vandenbe.MediaRemoteAdapter 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | MediaRemoteAdapter 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 0.1.0 23 | CFBundleShortVersionString 24 | 0.1 25 | CSResourcesFileMapped 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/Versions/A/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Resources/Info.plist 8 | 9 | M6AF1VWVJ1A/DSliCSjg170FqsY= 10 | 11 | 12 | files2 13 | 14 | Resources/Info.plist 15 | 16 | hash2 17 | 18 | z3yWmTAqjdrPJEZUQ+t6AVPhw0e/I8PAiVr0HIU2ivg= 19 | 20 | 21 | 22 | rules 23 | 24 | ^Resources/ 25 | 26 | ^Resources/.*\.lproj/ 27 | 28 | optional 29 | 30 | weight 31 | 1000 32 | 33 | ^Resources/.*\.lproj/locversion.plist$ 34 | 35 | omit 36 | 37 | weight 38 | 1100 39 | 40 | ^Resources/Base\.lproj/ 41 | 42 | weight 43 | 1010 44 | 45 | ^version.plist$ 46 | 47 | 48 | rules2 49 | 50 | .*\.dSYM($|/) 51 | 52 | weight 53 | 11 54 | 55 | ^(.*/)?\.DS_Store$ 56 | 57 | omit 58 | 59 | weight 60 | 2000 61 | 62 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 63 | 64 | nested 65 | 66 | weight 67 | 10 68 | 69 | ^.* 70 | 71 | ^Info\.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 20 77 | 78 | ^PkgInfo$ 79 | 80 | omit 81 | 82 | weight 83 | 20 84 | 85 | ^Resources/ 86 | 87 | weight 88 | 20 89 | 90 | ^Resources/.*\.lproj/ 91 | 92 | optional 93 | 94 | weight 95 | 1000 96 | 97 | ^Resources/.*\.lproj/locversion.plist$ 98 | 99 | omit 100 | 101 | weight 102 | 1100 103 | 104 | ^Resources/Base\.lproj/ 105 | 106 | weight 107 | 1010 108 | 109 | ^[^/]+$ 110 | 111 | nested 112 | 113 | weight 114 | 10 115 | 116 | ^embedded\.provisionprofile$ 117 | 118 | weight 119 | 20 120 | 121 | ^version\.plist$ 122 | 123 | weight 124 | 20 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Frameworks/MediaRemoteAdapter.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Hariharan Mudaliar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Updates/appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DynamicIsland 5 | https://github.com/Ebullioscopic/DynamicIsland/releases.atom 6 | Most recent updates to DynamicIsland 7 | en 8 | 9 | 10 | Version 1.0.0-beta 11 | https://github.com/Ebullioscopic/DynamicIsland 12 | 1 13 | 1.0.0-beta 14 | Initial Beta Release 16 |
    17 |
  • First beta release of DynamicIsland
  • 18 |
  • Dynamic Island interface for macOS
  • 19 |
  • Music playback controls
  • 20 |
  • Battery status monitoring
  • 21 |
  • Timer functionality
  • 22 |
  • Stats monitoring
  • 23 |
24 | ]]>
25 | Sun, 05 Oct 2025 00:49:55 +0530 26 | 34 | 15.0 35 |
36 | 37 | 63 | 64 |
65 |
66 | -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/MediaRemoteAdapter: -------------------------------------------------------------------------------- 1 | Versions/Current/MediaRemoteAdapter -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/Versions/A/MediaRemoteAdapter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/mediaremote-adapter/MediaRemoteAdapter.framework/Versions/A/MediaRemoteAdapter -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | MediaRemoteAdapter 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.vandenbe.MediaRemoteAdapter 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | MediaRemoteAdapter 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 0.1.0 23 | CFBundleShortVersionString 24 | 0.1 25 | CSResourcesFileMapped 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/Versions/A/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Resources/Info.plist 8 | 9 | M6AF1VWVJ1A/DSliCSjg170FqsY= 10 | 11 | 12 | files2 13 | 14 | Resources/Info.plist 15 | 16 | hash2 17 | 18 | z3yWmTAqjdrPJEZUQ+t6AVPhw0e/I8PAiVr0HIU2ivg= 19 | 20 | 21 | 22 | rules 23 | 24 | ^Resources/ 25 | 26 | ^Resources/.*\.lproj/ 27 | 28 | optional 29 | 30 | weight 31 | 1000 32 | 33 | ^Resources/.*\.lproj/locversion.plist$ 34 | 35 | omit 36 | 37 | weight 38 | 1100 39 | 40 | ^Resources/Base\.lproj/ 41 | 42 | weight 43 | 1010 44 | 45 | ^version.plist$ 46 | 47 | 48 | rules2 49 | 50 | .*\.dSYM($|/) 51 | 52 | weight 53 | 11 54 | 55 | ^(.*/)?\.DS_Store$ 56 | 57 | omit 58 | 59 | weight 60 | 2000 61 | 62 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 63 | 64 | nested 65 | 66 | weight 67 | 10 68 | 69 | ^.* 70 | 71 | ^Info\.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 20 77 | 78 | ^PkgInfo$ 79 | 80 | omit 81 | 82 | weight 83 | 20 84 | 85 | ^Resources/ 86 | 87 | weight 88 | 20 89 | 90 | ^Resources/.*\.lproj/ 91 | 92 | optional 93 | 94 | weight 95 | 1000 96 | 97 | ^Resources/.*\.lproj/locversion.plist$ 98 | 99 | omit 100 | 101 | weight 102 | 1100 103 | 104 | ^Resources/Base\.lproj/ 105 | 106 | weight 107 | 1010 108 | 109 | ^[^/]+$ 110 | 111 | nested 112 | 113 | weight 114 | 10 115 | 116 | ^embedded\.provisionprofile$ 117 | 118 | weight 119 | 20 120 | 121 | ^version\.plist$ 122 | 123 | weight 124 | 20 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /mediaremote-adapter/MediaRemoteAdapter.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /mediaremote-adapter/NowPlayingTestClient: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ebullioscopic/DynamicIsland/b509be88c13bfa7a4004a916e8172732e5faf1fd/mediaremote-adapter/NowPlayingTestClient --------------------------------------------------------------------------------