From 0badb998147135c75187a0c48f926b665af734c5 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 28 Jun 2026 02:53:51 +0800
Subject: [PATCH] Release 8.1.6 build 20260628.0230
---
src/Apptag/PreferencesView.swift | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 55 insertions(+), 1 deletions(-)
diff --git a/src/Apptag/PreferencesView.swift b/src/Apptag/PreferencesView.swift
index 52b09ab..97c2152 100644
--- a/src/Apptag/PreferencesView.swift
+++ b/src/Apptag/PreferencesView.swift
@@ -3,6 +3,8 @@
// MARK: - Preferences View
+private let themePreviewCountdownTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
+
private enum SettingsTab: String, CaseIterable, Identifiable {
case language
case general
@@ -85,6 +87,7 @@
@State private var selectedTab: SettingsTab = .general
@State private var selectedInitialLayoutMode: InitialLayoutMode = .smart
@State private var settingsProPromptFeature: ProFeature? = nil
+ @State private var themePreviewNow = Date()
init(initialTabRawValue: String? = nil) {
let initialTab = initialTabRawValue.flatMap(SettingsTab.init(rawValue:)) ?? .general
@@ -610,6 +613,10 @@
private var themeStatusSummary: String {
if proEntitlement.isPreviewingTheme {
+ if let countdown = themePreviewCountdownText {
+ return tr("pro.theme.status.previewCountdown")
+ .replacingOccurrences(of: "%time%", with: countdown)
+ }
return tr("pro.theme.status.preview")
}
switch proEntitlement.accessState {
@@ -635,7 +642,7 @@
private func themeOptionAccessory(for theme: AppGridTheme) -> ThemeOptionAccessory {
if let preview = proEntitlement.themePreviewState?.theme, preview == theme {
- return .pill(text: tr("pro.card.previewBadge"), style: .preview)
+ return .countdown(text: themePreviewCountdownText ?? "00:00")
}
if !proEntitlement.isUnlocked && theme.requiresPro {
return .pill(text: tr("pro.card.badge"), style: .locked)
@@ -647,6 +654,24 @@
proEntitlement.clearTransientOperationState()
withAnimation(.spring(response: 0.22, dampingFraction: 0.84)) {
settingsProPromptFeature = feature
+ }
+ }
+
+ private var themePreviewCountdownText: String? {
+ guard let preview = proEntitlement.themePreviewState else { return nil }
+ return Self.formatThemePreviewCountdown(seconds: preview.remainingSeconds(at: themePreviewNow))
+ }
+
+ private static func formatThemePreviewCountdown(seconds: Int) -> String {
+ let clamped = max(0, seconds)
+ return String(format: "%02d:%02d", clamped / 60, clamped % 60)
+ }
+
+ private func refreshThemePreviewCountdown(now: Date = Date()) {
+ themePreviewNow = now
+ guard let preview = proEntitlement.themePreviewState else { return }
+ if preview.remainingSeconds(at: now) <= 0 {
+ proEntitlement.stopThemePreview()
}
}
@@ -761,6 +786,14 @@
displayText: LauncherHotkey.quickSearch.displayString,
statusText: hotkeyStatusText(for: .quickSearch),
statusTone: hotkeyStatusTone(for: .quickSearch)
+ )
+
+ StaticHotkeyInfoRow(
+ title: tr("trackpadGesture.title"),
+ description: tr("trackpadGesture.description"),
+ displayText: "taglauncher://show",
+ statusText: tr("trackpadGesture.status"),
+ statusTone: .neutral
)
}
}
@@ -1473,6 +1506,10 @@
.onAppear {
syncSelectedLanguage()
showPendingHotkeyWarningIfNeeded()
+ refreshThemePreviewCountdown()
+ }
+ .onReceive(themePreviewCountdownTimer) { now in
+ refreshThemePreviewCountdown(now: now)
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherHotkeyRegistrationChanged)) { _ in
showPendingHotkeyWarningIfNeeded()
@@ -1662,6 +1699,7 @@
private enum ThemeOptionAccessory {
case checkmark
case circle
+ case countdown(text: String)
case pill(text: String, style: ProStatusPillStyle)
}
@@ -1739,6 +1777,22 @@
Image(systemName: "circle")
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(Color.secondary.opacity(0.42))
+ case .countdown(let text):
+ Text(text)
+ .font(.system(size: 10, weight: .semibold, design: .monospaced))
+ .foregroundStyle(Color(red: 0.30, green: 0.22, blue: 0.08))
+ .lineLimit(1)
+ .frame(minWidth: 42)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 4)
+ .background(
+ Capsule(style: .continuous)
+ .fill(Color.orange.opacity(0.14))
+ )
+ .overlay(
+ Capsule(style: .continuous)
+ .stroke(Color.orange.opacity(0.28), lineWidth: 1)
+ )
case .pill(let text, let style):
ProStatusPill(text: text, style: style, compact: true)
}
--
Gitblit v1.9.3