Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/update-appcast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
ref: main
token: ${{ secrets.APPCAST_TOKEN }}

- name: Install jq
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq
sudo apt-get install -y jq pandoc

- name: Generate appcast files
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq jq curl
sudo apt-get install -y -qq jq curl pandoc

- name: Create output directories
run: mkdir -p Docs/signatures
Expand Down
1,277 changes: 611 additions & 666 deletions Docs/appcast-beta.xml

Large diffs are not rendered by default.

999 changes: 478 additions & 521 deletions Docs/appcast.xml

Large diffs are not rendered by default.

23 changes: 19 additions & 4 deletions Sources/TermQ/TermQApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ class TermQAppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
/// Sparkle updater controller for automatic updates
let updaterController: SPUStandardUpdaterController

/// Updater view model for SwiftUI
let updaterViewModel: UpdaterViewModel

/// Reference to the main window (first window created)
private var mainWindow: NSWindow?

Expand All @@ -339,6 +342,10 @@ class TermQAppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
updaterDelegate: sparkleDelegate,
userDriverDelegate: nil
)
updaterViewModel = UpdaterViewModel(
updater: updaterController.updater,
controller: updaterController
)
super.init()
}

Expand Down Expand Up @@ -636,6 +643,7 @@ struct TermQApp: App {

Settings {
SettingsView()
.environmentObject(appDelegate.updaterViewModel)
}

Window("TermQ Help", id: "help") {
Expand Down Expand Up @@ -675,15 +683,16 @@ struct IdentifiableURL: Identifiable {
/// Observable wrapper for Sparkle's updater to use in SwiftUI
@MainActor
final class UpdaterViewModel: ObservableObject {
/// Shared instance - initialized lazily when first accessed
/// Shared instance - accessed from app delegate
static var shared: UpdaterViewModel? {
guard let appDelegate = NSApp.delegate as? TermQAppDelegate else {
return nil
}
return UpdaterViewModel(updater: appDelegate.updaterController.updater)
return appDelegate.updaterViewModel
}

private let updater: SPUUpdater
private let controller: SPUStandardUpdaterController?
private var cancellables = Set<AnyCancellable>()

/// Whether automatic update checks are enabled (defaults to true)
Expand All @@ -704,8 +713,9 @@ final class UpdaterViewModel: ObservableObject {
}
}

init(updater: SPUUpdater) {
init(updater: SPUUpdater, controller: SPUStandardUpdaterController? = nil) {
self.updater = updater
self.controller = controller
// Default to true for automatic checks if not previously set
let hasExistingPreference = UserDefaults.standard.object(forKey: "SUAutomaticallyChecksForUpdates") != nil
if !hasExistingPreference {
Expand All @@ -726,7 +736,12 @@ final class UpdaterViewModel: ObservableObject {

/// Manually check for updates
func checkForUpdates() {
updater.checkForUpdates()
// Use the controller's action method if available (same path as Menu Bar)
if let controller = controller {
controller.checkForUpdates(nil)
} else {
updater.checkForUpdates()
}
}
}

Expand Down
1 change: 0 additions & 1 deletion Sources/TermQ/Views/Settings/SettingsGeneralView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ struct SettingsGeneralView: View {
Button(Strings.Settings.checkForUpdates) {
updaterViewModel.checkForUpdates()
}
.disabled(!updaterViewModel.canCheckForUpdates)
}
} header: {
Text(Strings.Settings.sectionUpdates)
Expand Down
12 changes: 2 additions & 10 deletions Sources/TermQ/Views/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,8 @@ struct SettingsView: View {
// Data directory
@State private var dataDirectory: String = DataDirectoryManager.displayPath

// Updater (Sparkle)
@StateObject private var updaterViewModel =
UpdaterViewModel.shared
?? UpdaterViewModel(
updater: SPUStandardUpdaterController(
startingUpdater: false,
updaterDelegate: nil,
userDriverDelegate: nil
).updater
)
// Updater (Sparkle) - injected from app level
@EnvironmentObject var updaterViewModel: UpdaterViewModel

enum SettingsTab: CaseIterable {
case general
Expand Down
26 changes: 24 additions & 2 deletions scripts/generate-appcast.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ check_dependencies() {
log_info "Install with: brew install ${missing[*]}"
exit 1
fi

# Check for pandoc (optional, for Markdown to HTML conversion)
if ! command -v pandoc &> /dev/null; then
log_warn "pandoc not found - release notes will display as plain Markdown"
log_info "For better formatting, install with: brew install pandoc"
fi
}

# Fetch releases from GitHub API
Expand Down Expand Up @@ -101,6 +107,22 @@ is_prerelease() {
echo "false"
}

# Convert Markdown to HTML if pandoc is available
markdown_to_html() {
local markdown="$1"

if command -v pandoc &> /dev/null; then
# Use pandoc to convert Markdown to HTML
# --from=gfm: GitHub-flavored Markdown
# --to=html: Convert to HTML
# --wrap=none: Don't wrap lines
echo "$markdown" | pandoc --from=gfm --to=html --wrap=none 2>/dev/null || echo "$markdown"
else
# Fallback: escape HTML entities for plain text display
echo "$markdown" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g'
fi
}

# Generate XML for a single release item
generate_item() {
local tag="$1"
Expand All @@ -114,9 +136,9 @@ generate_item() {
local version
version=$(extract_version "$tag")

# Escape HTML entities in body for CDATA
# Convert Markdown to HTML (or escape for plain text if pandoc unavailable)
local description
description=$(echo "$body" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
description=$(markdown_to_html "$body")

cat << EOF
<item>
Expand Down
Loading