From cfb93f5127af692ff74178329a8b635efc894f8b Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 28 Jun 2026 03:16:25 +0800
Subject: [PATCH] Archive App Store release 8.1.6 build 20260628.0230
---
src/Apptag/PreferencesView.swift | 165 +++++++++++++++++++++++++++++++++++++------------------
1 files changed, 111 insertions(+), 54 deletions(-)
diff --git a/src/Apptag/PreferencesView.swift b/src/Apptag/PreferencesView.swift
index 0ac3ceb..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
@@ -578,7 +581,7 @@
}
switch proEntitlement.accessState {
case .free:
- return .locked
+ return .neutral
case .purchased:
return .unlocked
case .legacy:
@@ -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()
}
}
@@ -762,6 +787,14 @@
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
+ )
}
}
@@ -803,7 +836,7 @@
)
}
- private var dataProStatusCard: some View {
+ private var proStatusCard: some View {
HStack(alignment: .top, spacing: 18) {
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 10) {
@@ -1139,8 +1172,6 @@
Spacer(minLength: 36)
VStack(alignment: .center, spacing: 24) {
- dataProStatusCard
-
dataSection(minHeight: 124) {
HStack(alignment: .center, spacing: 12) {
dataSectionTitle(tr("settings.initialLayout"))
@@ -1291,62 +1322,67 @@
case .about:
// Tab 6: About
VStack(spacing: 0) {
- Spacer(minLength: 72)
+ Spacer(minLength: 44)
- HStack {
- if let icon = NSImage(named: NSImage.applicationIconName) {
- Image(nsImage: icon)
- .resizable()
- .frame(width: 128, height: 128)
- }
- VStack(alignment: .leading, spacing: 4) {
- Text("TagLauncher")
- .font(.title2)
- .fontWeight(.semibold)
- Text(tr("app.description"))
- .font(.body)
- .foregroundStyle(.secondary)
- Text("\(tr("app.version")) \(appVersion) (\(tr("app.build")) \(buildVersion))")
- .font(.callout)
- .foregroundStyle(.tertiary)
+ VStack(alignment: .center, spacing: 24) {
+ proStatusCard
- Divider()
- .padding(.vertical, 4)
-
- HStack(spacing: 8) {
- Button {
- NSWorkspace.shared.open(helpPDFURL)
- } label: {
- Label(tr("help.openPDF"), systemImage: "questionmark.circle")
- }
- .buttonStyle(.borderedProminent)
- .controlSize(.regular)
-
- Button {
- NSPasteboard.general.clearContents()
- NSPasteboard.general.setString(helpPDFURL.absoluteString, forType: .string)
- } label: {
- Image(systemName: "link")
- }
- .buttonStyle(.bordered)
- .controlSize(.regular)
- .help(tr("help.copyLink"))
+ HStack(alignment: .center, spacing: 24) {
+ if let icon = NSImage(named: NSImage.applicationIconName) {
+ Image(nsImage: icon)
+ .resizable()
+ .frame(width: 112, height: 112)
}
+ VStack(alignment: .leading, spacing: 4) {
+ Text("TagLauncher")
+ .font(.title2)
+ .fontWeight(.semibold)
+ Text(tr("app.description"))
+ .font(.body)
+ .foregroundStyle(.secondary)
+ Text("\(tr("app.version")) \(appVersion) (\(tr("app.build")) \(buildVersion))")
+ .font(.callout)
+ .foregroundStyle(.tertiary)
- Text(tr("help.currentLanguage"))
- .font(.caption)
- .foregroundStyle(.secondary)
- .padding(.bottom, 2)
+ Divider()
+ .padding(.vertical, 4)
- Text("万物之中,希望最美")
- .font(.caption)
- .foregroundStyle(.secondary)
- Text("永桔@shanghai3168@gmail.com")
- .font(.caption)
- .foregroundStyle(.secondary)
+ HStack(spacing: 8) {
+ Button {
+ NSWorkspace.shared.open(helpPDFURL)
+ } label: {
+ Label(tr("help.openPDF"), systemImage: "questionmark.circle")
+ }
+ .buttonStyle(.borderedProminent)
+ .controlSize(.regular)
+
+ Button {
+ NSPasteboard.general.clearContents()
+ NSPasteboard.general.setString(helpPDFURL.absoluteString, forType: .string)
+ } label: {
+ Image(systemName: "link")
+ }
+ .buttonStyle(.bordered)
+ .controlSize(.regular)
+ .help(tr("help.copyLink"))
+ }
+
+ Text(tr("help.currentLanguage"))
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .padding(.bottom, 2)
+
+ Text("万物之中,希望最美")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ Text("永桔@shanghai3168@gmail.com")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
}
+ .frame(width: dataPanelWidth, alignment: .leading)
}
- .frame(width: 560, alignment: .leading)
+ .frame(width: dataPanelWidth, alignment: .center)
Spacer(minLength: 30)
Spacer(minLength: 18)
@@ -1470,6 +1506,10 @@
.onAppear {
syncSelectedLanguage()
showPendingHotkeyWarningIfNeeded()
+ refreshThemePreviewCountdown()
+ }
+ .onReceive(themePreviewCountdownTimer) { now in
+ refreshThemePreviewCountdown(now: now)
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherHotkeyRegistrationChanged)) { _ in
showPendingHotkeyWarningIfNeeded()
@@ -1659,6 +1699,7 @@
private enum ThemeOptionAccessory {
case checkmark
case circle
+ case countdown(text: String)
case pill(text: String, style: ProStatusPillStyle)
}
@@ -1736,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