diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddbb2bc..1af5804 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.6.2] - 2026-06-08
+
+### Fixes
+- Fix keyboard shortcut collision that caused the main window to intercept Enter/Escape keys when the duplicate resolution sheet was open.
+- Fix UI freeze when checking release notes for an update by parsing HTML asynchronously on a background thread.
+- Improve Sparkle changelog formatting by converting HTML tags to clean Markdown instead of stripping them into an unreadable block of text.
+- Change the internal `Process xxxxx` status message to a cleaner `Starting...` message when queueing a new download.
+- Fix `EXC_BREAKPOINT` crash on app launch in production builds by prioritizing `Bundle.main` over `Bundle.module` when accessing resources.
+
## [0.6.1] - 2026-06-08
### New Features
diff --git a/Sources/Firelink/AddDownloadsView.swift b/Sources/Firelink/AddDownloadsView.swift
index 4e4f8ef..e6d830e 100644
--- a/Sources/Firelink/AddDownloadsView.swift
+++ b/Sources/Firelink/AddDownloadsView.swift
@@ -312,7 +312,7 @@ struct AddDownloadsView: View {
Button("Cancel") {
dismiss()
}
- .keyboardShortcut(.cancelAction)
+ .keyboardShortcut(showingDuplicates ? nil : .cancelAction)
Button("Add to Queue") {
addDownloads(start: false)
@@ -324,7 +324,7 @@ struct AddDownloadsView: View {
}
.buttonStyle(.borderedProminent)
.disabled(!canAddDownloads)
- .keyboardShortcut(.defaultAction)
+ .keyboardShortcut(showingDuplicates ? nil : .defaultAction)
}
}
diff --git a/Sources/Firelink/DownloadController.swift b/Sources/Firelink/DownloadController.swift
index 870ad86..230ede9 100644
--- a/Sources/Firelink/DownloadController.swift
+++ b/Sources/Firelink/DownloadController.swift
@@ -602,7 +602,7 @@ final class DownloadController: ObservableObject {
update(item.id) {
$0.rpcPort = handle.rpcPort
$0.rpcSecret = handle.rpcSecret
- $0.message = "Process \(handle.processIdentifier)"
+ $0.message = "Starting..."
}
saveDownloads()
applySpeedLimitsToActiveDownloads()
diff --git a/Sources/Firelink/MediaEngineManager.swift b/Sources/Firelink/MediaEngineManager.swift
index f718143..82b825e 100644
--- a/Sources/Firelink/MediaEngineManager.swift
+++ b/Sources/Firelink/MediaEngineManager.swift
@@ -31,11 +31,19 @@ final class MediaEngineManager: ObservableObject {
}
func binaryPath(for addon: AddonType) -> URL? {
- for bundle in [Bundle.main, Bundle.module] {
- if let bundled = bundle.url(forResource: addon.binaryName, withExtension: nil),
+ if let bundled = Bundle.main.url(forResource: addon.binaryName, withExtension: nil),
+ FileManager.default.isExecutableFile(atPath: bundled.path) {
+ return bundled
+ }
+
+ // Prevent fatalError crash: avoid accessing Bundle.module if running in a packaged app.
+ if Bundle.main.bundleURL.pathExtension.lowercased() != "app" {
+ #if SWIFT_PACKAGE
+ if let bundled = Bundle.module.url(forResource: addon.binaryName, withExtension: nil),
FileManager.default.isExecutableFile(atPath: bundled.path) {
return bundled
}
+ #endif
}
return nil
}
diff --git a/Sources/Firelink/Settings/InlineUpdateUserDriver.swift b/Sources/Firelink/Settings/InlineUpdateUserDriver.swift
index 3f7e31a..d4c3941 100644
--- a/Sources/Firelink/Settings/InlineUpdateUserDriver.swift
+++ b/Sources/Firelink/Settings/InlineUpdateUserDriver.swift
@@ -32,19 +32,53 @@ class InlineUpdateUserDriver: NSObject, SPUUserDriver {
}
func showUpdateReleaseNotes(with downloadData: SPUDownloadData) {
- DispatchQueue.main.async {
+ DispatchQueue.global(qos: .userInitiated).async {
if let htmlString = String(data: downloadData.data, encoding: .utf8) {
- self.updater?.releaseNotes = self.stripHTML(htmlString)
+ let parsedText = self.fastHTMLToMarkdown(htmlString)
+ DispatchQueue.main.async {
+ self.updater?.releaseNotes = parsedText
+ }
}
}
}
- private func stripHTML(_ string: String) -> String {
- guard let data = string.data(using: .utf8) else { return string }
- if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) {
- return attributedString.string.trimmingCharacters(in: .whitespacesAndNewlines)
+ nonisolated private func fastHTMLToMarkdown(_ html: String) -> String {
+ var text = html
+ text = text.replacingOccurrences(of: "
", with: "\n", options: .caseInsensitive)
+ text = text.replacingOccurrences(of: "
", with: "\n", options: .caseInsensitive)
+ text = text.replacingOccurrences(of: "
", with: "\n", options: .caseInsensitive)
+ text = text.replacingOccurrences(of: "