Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
All notable changes to Chorus are documented here. Format loosely follows
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added

- Chorus can open mail links from macOS in a floating window for the account you choose. Choose Save and detect mail links in that service’s settings, then approve its page for new drafts. Links clicked inside services still use the Mac’s default mail app.

### Changed

- Chorus tells sites it is Safari 27 rather than 26 unless the service has a custom browser setting.

### Fixed

- Hiding Chorus and switching services happen at once. Chorus gives recent page actions a short time to finish before clearing hover or putting the page aside. This does not confirm a server save: reloading or quitting immediately after a click can still lose an unsent change.
- Service tabs no longer cover the top of the page. Returning from Hide cancels its delayed hover exit, and repeated reload requests share one pending reload.
- Chorus no longer crashes on macOS 14 when a page action returns no result.

- Mail compose windows stay open when an editor disappears and hide with their popups when Chorus locks.

## [1.5.28] - 2026-10-11

### Changed
Expand Down
96 changes: 96 additions & 0 deletions Chorus.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions Chorus/App/AppDependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import SwiftData
import WebKit

/// Construction only. Persistence recovery, observers and preloading belong to
/// the production launch path, not to these shared dependencies.
@MainActor
struct AppDependencies {
let dataStoreManager: DataStoreManager
let userScriptManager: UserScriptManager
let badgeManager: BadgeManager
let notificationManager: NotificationManager
let transientBadgeFetcher: TransientBadgeFetcher
let contentBlocker: ContentBlockerManager
let webViewPool: WebViewPool
let networkMonitor: NetworkMonitor

init(
modelContainer: ModelContainer,
dataStoreManager: DataStoreManager,
loadMailProbe: @escaping (WKWebView, URL) -> Void = { view, url in view.load(URLRequest(url: url)) },
loadMailComposer: @escaping (WKWebView, URL) -> Void = { view, url in view.load(URLRequest(url: url)) }
) {
self.dataStoreManager = dataStoreManager
let scripts = UserScriptManager()
let badges = BadgeManager()
userScriptManager = scripts
badgeManager = badges
scripts.isServiceMuted = { @Sendable serviceID in
MainActor.assumeIsolated {
var descriptor = FetchDescriptor<ServiceInstance>(predicate: #Predicate { $0.id == serviceID })
descriptor.fetchLimit = 1
return (try? modelContainer.mainContext.fetch(descriptor).first)?.isEffectivelyMuted ?? false
}
}
scripts.isServiceNotifyingOS = { @Sendable serviceID in
MainActor.assumeIsolated {
var descriptor = FetchDescriptor<ServiceInstance>(predicate: #Predicate { $0.id == serviceID })
descriptor.fetchLimit = 1
return (try? modelContainer.mainContext.fetch(descriptor).first)?.notifiesOSEffective ?? false
}
}
scripts.isDoNotDisturbActive = { @Sendable in
MainActor.assumeIsolated { badges.doNotDisturb }
}
notificationManager = NotificationManager(badgeManager: badges)
transientBadgeFetcher = TransientBadgeFetcher(badgeManager: badges, dataStoreManager: dataStoreManager)
let blocker = ContentBlockerManager()
contentBlocker = blocker
webViewPool = WebViewPool(
dataStoreManager: dataStoreManager, userScriptManager: scripts,
contentBlocker: blocker, loadMailProbe: loadMailProbe, loadMailComposer: loadMailComposer
)
networkMonitor = NetworkMonitor()
}
}
Loading