From ab59aa3a800139e2fe80f7e802dfada2258ab0f6 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 25 Jun 2026 16:24:01 +0800
Subject: [PATCH] Add usage tips close reminder
---
src/Apptag/Localization/fr.json | 6
src/Apptag/Localization/ru.json | 6
src/Apptag/AppGridCollectionView.swift | 7
src/Scripts/usage_tips_qa.sh | 59 +++
src/Apptag/Localization/ar.json | 6
src/Apptag/Localization/ro.json | 6
src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-worklog.md | 12
src/Apptag/Localization/en.json | 6
src/Apptag/Localization/sv.json | 6
src/Apptag/Localization/zh-Hant.json | 6
src/Apptag/Localization/pl.json | 6
src/Apptag/Localization/es.json | 6
src/Apptag/Localization/th.json | 6
src/Apptag/Localization/nn.json | 6
src/CHANGELOG.md | 6
src/Apptag/Info.plist | 4
src/Apptag/Localization/cs.json | 6
src/Apptag/Localization/ms.json | 6
src/Apptag/Localization/it.json | 6
src/Apptag/Localization/tr.json | 6
src/Apptag/Localization/da.json | 6
src/Apptag/Localization/id.json | 6
src/Scripts/usage_tips_close_reminder_qa.sh | 125 ++++++++
src/Apptag/Localization/ja.json | 6
src/Apptag/AppDefaults.swift | 1
src/Apptag/EditModeViews.swift | 215 ++++++++++++++
src/TODO.md | 8
src/Apptag/Localization/de.json | 6
src/Apptag/Localization/pt-BR.json | 6
src/Apptag/Localization/ar-Najdi.json | 6
src/Apptag/Localization/nb.json | 6
src/Apptag/Localization/ko.json | 6
src/Apptag/Localization/no.json | 6
src/Apptag/ContentView.swift | 200 +++++++++---
src/Apptag/Localization/sr-Cyrl.json | 6
src/Apptag/Localization/zh-Hans.json | 6
src/Apptag/Localization/nl.json | 6
src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-todo.md | 48 +++
src/Apptag/Localization/uk.json | 6
src/Apptag/Localization/vi.json | 6
40 files changed, 759 insertions(+), 100 deletions(-)
diff --git a/src/Apptag/AppDefaults.swift b/src/Apptag/AppDefaults.swift
index 8770f79..47f9c8a 100644
--- a/src/Apptag/AppDefaults.swift
+++ b/src/Apptag/AppDefaults.swift
@@ -32,6 +32,7 @@
"useAppKitTagNavigation": useAppKitTagNavigation,
"skipTagRemovalDropConfirm": false,
"skipUncategorizedDropConfirm": false,
+ "skipUsageTipsCloseReminder": false,
LauncherHotkeyRegistrationStore.mainStateKey: LauncherHotkeyRegistrationState.active.rawValue,
LauncherHotkeyRegistrationStore.quickSearchStateKey: LauncherHotkeyRegistrationState.active.rawValue
])
diff --git a/src/Apptag/AppGridCollectionView.swift b/src/Apptag/AppGridCollectionView.swift
index 7ee1432..d124a61 100644
--- a/src/Apptag/AppGridCollectionView.swift
+++ b/src/Apptag/AppGridCollectionView.swift
@@ -1145,7 +1145,7 @@
detailScrollView.borderType = .noBorder
detailScrollView.documentView = detailLabel
- closeButton.action = { [weak self] in self?.hideUsageTips() }
+ closeButton.action = { [weak self] in self?.requestHideUsageTips() }
previousButton.action = { [weak self] in self?.selectUsageTip(offset: -1) }
nextButton.action = { [weak self] in self?.selectUsageTip(offset: 1) }
@@ -1205,10 +1205,9 @@
applyCoordinatorState()
}
- private func hideUsageTips() {
+ private func requestHideUsageTips() {
Diagnostics.log("usageTips.hud.close")
clearHoverState()
- isHidden = true
coordinator?.onHideUsageTips()
}
@@ -1217,7 +1216,7 @@
let closeHitOutset: CGFloat = 8
if closeButton.frame.insetBy(dx: -closeHitOutset, dy: -closeHitOutset).contains(point) {
closeButton.performPressFeedback()
- hideUsageTips()
+ requestHideUsageTips()
return true
}
let hitOutset: CGFloat = 6
diff --git a/src/Apptag/ContentView.swift b/src/Apptag/ContentView.swift
index 9f0743c..7ab2869 100644
--- a/src/Apptag/ContentView.swift
+++ b/src/Apptag/ContentView.swift
@@ -328,6 +328,8 @@
var pendingTagRemovalDrop: PendingTagRemovalDrop? = nil
var uncategorizedDropSuppressFuturePrompt = false
var tagRemovalDropSuppressFuturePrompt = false
+ var usageTipsCloseReminderVisible = false
+ var usageTipsCloseSuppressFuturePrompt = false
var appDragResetToken = 0
var bubbleDraftNote = ""
var tagNavigationHoveredGroupName: String? = nil
@@ -438,6 +440,7 @@
@AppStorage("useAppKitTagNavigation") private var useAppKitTagNavigation = AppDefaults.useAppKitTagNavigation
@AppStorage("skipTagRemovalDropConfirm") private var skipTagRemovalDropConfirm = false
@AppStorage("skipUncategorizedDropConfirm") private var skipUncategorizedDropConfirm = false
+ @AppStorage("skipUsageTipsCloseReminder") private var skipUsageTipsCloseReminder = false
private let editSidebarWidth: CGFloat = 188
private let editSidebarHorizontalInset: CGFloat = 12
@@ -449,6 +452,7 @@
|| appGridInteraction.appDragModeActive
|| appGridInteraction.pendingUncategorizedDrop != nil
|| appGridInteraction.pendingTagRemovalDrop != nil
+ || appGridInteraction.usageTipsCloseReminderVisible
}
private var appGridHighlightedGroupName: String? {
appGridInteraction.tagNavigationHoveredGroupName
@@ -514,68 +518,13 @@
}
if shouldRenderAppGridBehindQuickSearch {
- if notchHeight > 0 {
- VStack {
- Rectangle().fill(.black)
- .frame(height: notchHeight)
- .ignoresSafeArea(edges: .top)
- Spacer()
- }
- .allowsHitTesting(false)
- }
-
- switch editPhase {
- case .none:
- normalContent
- case .editingTags:
- editTagsView
- case .editingApps:
- editAppsView
- }
-
- uncommonAppBubbleOverlay
- smartStartNoticeOverlay
- editActionFeedbackOverlay
- uncategorizedDropConfirmOverlay
- tagRemovalDropConfirmOverlay
+ appGridInteractionLayer
}
quickSearchOverlay
- if shouldRenderAppGridBehindQuickSearch, let message = appGridInteraction.dropWarningToast {
- Text(message)
- .font(.system(size: 16, weight: .semibold))
- .foregroundStyle(.primary)
- .padding(.horizontal, 22)
- .padding(.vertical, 12)
- .background(
- RoundedRectangle(cornerRadius: 10)
- .fill(.ultraThickMaterial)
- .shadow(color: .black.opacity(0.22), radius: 18, y: 10)
- )
- .transition(.scale(scale: 0.96).combined(with: .opacity))
- .allowsHitTesting(false)
- }
-
- if shouldRenderAppGridBehindQuickSearch && appGridInteraction.dropRefreshVisible {
- Color.black.opacity(0.08)
- .ignoresSafeArea()
- .transition(.opacity)
- .allowsHitTesting(false)
-
- ProgressView()
- .progressViewStyle(.circular)
- .controlSize(.large)
- .scaleEffect(1.15)
- .padding(24)
- .background(
- RoundedRectangle(cornerRadius: 12)
- .fill(.ultraThickMaterial)
- .shadow(color: .black.opacity(0.26), radius: 24, y: 12)
- )
- .transition(.scale(scale: 0.92).combined(with: .opacity))
- .allowsHitTesting(false)
- }
+ dropWarningToastOverlay
+ dropRefreshOverlay
}
.onAppear {
refreshNotchHeight()
@@ -681,6 +630,76 @@
}
}
+ @ViewBuilder
+ private var appGridInteractionLayer: some View {
+ if notchHeight > 0 {
+ VStack {
+ Rectangle().fill(.black)
+ .frame(height: notchHeight)
+ .ignoresSafeArea(edges: .top)
+ Spacer()
+ }
+ .allowsHitTesting(false)
+ }
+
+ switch editPhase {
+ case .none:
+ normalContent
+ case .editingTags:
+ editTagsView
+ case .editingApps:
+ editAppsView
+ }
+
+ uncommonAppBubbleOverlay
+ smartStartNoticeOverlay
+ editActionFeedbackOverlay
+ uncategorizedDropConfirmOverlay
+ tagRemovalDropConfirmOverlay
+ usageTipsCloseReminderOverlay
+ }
+
+ @ViewBuilder
+ private var dropWarningToastOverlay: some View {
+ if shouldRenderAppGridBehindQuickSearch, let message = appGridInteraction.dropWarningToast {
+ Text(message)
+ .font(.system(size: 16, weight: .semibold))
+ .foregroundStyle(.primary)
+ .padding(.horizontal, 22)
+ .padding(.vertical, 12)
+ .background(
+ RoundedRectangle(cornerRadius: 10)
+ .fill(.ultraThickMaterial)
+ .shadow(color: .black.opacity(0.22), radius: 18, y: 10)
+ )
+ .transition(.scale(scale: 0.96).combined(with: .opacity))
+ .allowsHitTesting(false)
+ }
+ }
+
+ @ViewBuilder
+ private var dropRefreshOverlay: some View {
+ if shouldRenderAppGridBehindQuickSearch && appGridInteraction.dropRefreshVisible {
+ Color.black.opacity(0.08)
+ .ignoresSafeArea()
+ .transition(.opacity)
+ .allowsHitTesting(false)
+
+ ProgressView()
+ .progressViewStyle(.circular)
+ .controlSize(.large)
+ .scaleEffect(1.15)
+ .padding(24)
+ .background(
+ RoundedRectangle(cornerRadius: 12)
+ .fill(.ultraThickMaterial)
+ .shadow(color: .black.opacity(0.26), radius: 24, y: 12)
+ )
+ .transition(.scale(scale: 0.92).combined(with: .opacity))
+ .allowsHitTesting(false)
+ }
+ }
+
private func publishModalInteractionState() {
NotificationCenter.default.post(
name: .tagLauncherModalInteractionChanged,
@@ -689,6 +708,7 @@
"active": smartStartNotice != nil
|| appGridInteraction.pendingUncategorizedDrop != nil
|| appGridInteraction.pendingTagRemovalDrop != nil
+ || appGridInteraction.usageTipsCloseReminderVisible
]
)
}
@@ -1242,7 +1262,7 @@
},
onScrollActivity: handleAppGridScrollActivity,
onDragModeChange: { setAppDragMode($0) },
- onHideUsageTips: { hideUsageTips = true },
+ onHideUsageTips: requestHideUsageTips,
onUsageTipsHoverChange: handleUsageTipsHoverChange
)
}
@@ -1689,6 +1709,34 @@
}
.ignoresSafeArea()
.allowsHitTesting(appGridInteraction.pendingTagRemovalDrop != nil)
+ }
+
+ private var usageTipsCloseReminderOverlay: some View {
+ GeometryReader { proxy in
+ if appGridInteraction.usageTipsCloseReminderVisible {
+ ZStack {
+ Color.black.opacity(0.14)
+ .ignoresSafeArea()
+
+ UsageTipsCloseReminderBubble(
+ title: tr("usageTips.closeReminder.title"),
+ message: tr("usageTips.closeReminder.message"),
+ previewCallout: tr("usageTips.closeReminder.previewCallout"),
+ doNotRemindTitle: tr("usageTips.closeReminder.doNotRemind"),
+ doNotRemind: $appGridInteraction.usageTipsCloseSuppressFuturePrompt,
+ confirmTitle: tr("edit.confirm"),
+ onCancel: dismissUsageTipsCloseReminder,
+ onConfirm: confirmUsageTipsCloseReminder
+ )
+ .frame(width: min(760, max(420, proxy.size.width - 120)))
+ .position(x: proxy.size.width / 2, y: proxy.size.height / 2)
+ .transition(.scale(scale: 0.94).combined(with: .opacity))
+ }
+ .zIndex(712)
+ }
+ }
+ .ignoresSafeArea()
+ .allowsHitTesting(appGridInteraction.usageTipsCloseReminderVisible)
}
private func buildEditActionFeedback(for selectedApps: [AppInfo], tags: [String]) -> EditActionFeedback {
@@ -2496,6 +2544,40 @@
}
}
+ private func requestHideUsageTips() {
+ guard !hideUsageTips else { return }
+ guard !skipUsageTipsCloseReminder else {
+ hideUsageTips = true
+ return
+ }
+ clearAppBubbleState()
+ appGridInteraction.usageTipsCloseSuppressFuturePrompt = false
+ withAnimation(.spring(response: 0.24, dampingFraction: 0.84)) {
+ appGridInteraction.usageTipsCloseReminderVisible = true
+ }
+ publishModalInteractionState()
+ }
+
+ private func dismissUsageTipsCloseReminder() {
+ appGridInteraction.usageTipsCloseSuppressFuturePrompt = false
+ withAnimation(.easeOut(duration: 0.18)) {
+ appGridInteraction.usageTipsCloseReminderVisible = false
+ }
+ publishModalInteractionState()
+ }
+
+ private func confirmUsageTipsCloseReminder() {
+ if appGridInteraction.usageTipsCloseSuppressFuturePrompt {
+ skipUsageTipsCloseReminder = true
+ }
+ appGridInteraction.usageTipsCloseSuppressFuturePrompt = false
+ withAnimation(.easeOut(duration: 0.16)) {
+ appGridInteraction.usageTipsCloseReminderVisible = false
+ }
+ hideUsageTips = true
+ publishModalInteractionState()
+ }
+
private func removeTagFromDroppedApp(app: AppInfo, tagName: String) {
guard isRemovableRegularTag(tagName),
appHasTag(app, tagName: tagName)
diff --git a/src/Apptag/EditModeViews.swift b/src/Apptag/EditModeViews.swift
index b56b97a..9a7ff79 100644
--- a/src/Apptag/EditModeViews.swift
+++ b/src/Apptag/EditModeViews.swift
@@ -621,3 +621,218 @@
.onExitCommand(perform: onCancel)
}
}
+
+struct UsageTipsCloseReminderBubble: View {
+ let title: String
+ let message: String
+ let previewCallout: String
+ let doNotRemindTitle: String
+ @Binding var doNotRemind: Bool
+ let confirmTitle: String
+ let onCancel: () -> Void
+ let onConfirm: () -> Void
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 16) {
+ HStack(alignment: .top, spacing: 12) {
+ Text(title)
+ .font(.system(size: 22, weight: .bold))
+ .foregroundStyle(.white)
+ .fixedSize(horizontal: false, vertical: true)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Button(action: onCancel) {
+ Image(systemName: "xmark")
+ .font(.system(size: 12, weight: .bold))
+ .foregroundStyle(.white.opacity(0.82))
+ .frame(width: 26, height: 26)
+ .background(Circle().fill(Color.white.opacity(0.12)))
+ }
+ .buttonStyle(.plain)
+ .help(tr("tag.cancel"))
+ }
+
+ UsageTipsCloseReminderSettingsPreview(callout: previewCallout)
+
+ Text(message)
+ .font(.system(size: 15, weight: .medium))
+ .lineSpacing(4)
+ .foregroundStyle(.white.opacity(0.84))
+ .multilineTextAlignment(.leading)
+ .fixedSize(horizontal: false, vertical: true)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Button {
+ doNotRemind.toggle()
+ } label: {
+ HStack(spacing: 10) {
+ ZStack {
+ RoundedRectangle(cornerRadius: 4, style: .continuous)
+ .fill(doNotRemind ? Color.accentColor : Color.white.opacity(0.10))
+ RoundedRectangle(cornerRadius: 4, style: .continuous)
+ .stroke(Color.white.opacity(0.92), lineWidth: 1.6)
+ if doNotRemind {
+ Image(systemName: "checkmark")
+ .font(.system(size: 12, weight: .bold))
+ .foregroundStyle(.white)
+ }
+ }
+ .frame(width: 18, height: 18)
+
+ Text(doNotRemindTitle)
+ .font(.system(size: 13, weight: .medium))
+ .foregroundStyle(.white.opacity(0.82))
+ .lineLimit(2)
+ .multilineTextAlignment(.leading)
+ }
+ .contentShape(Rectangle())
+ }
+ .buttonStyle(.plain)
+
+ HStack {
+ Spacer(minLength: 0)
+
+ Button(action: onConfirm) {
+ Text(confirmTitle)
+ .font(.system(size: 13, weight: .semibold))
+ .foregroundStyle(.white)
+ .frame(minWidth: 96)
+ .frame(height: 32)
+ .padding(.horizontal, 10)
+ .background(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .fill(Color.accentColor)
+ )
+ }
+ .buttonStyle(.plain)
+ }
+ }
+ .padding(.horizontal, 24)
+ .padding(.vertical, 20)
+ .background(
+ RoundedRectangle(cornerRadius: 18, style: .continuous)
+ .fill(Color.black.opacity(0.92))
+ .shadow(color: .black.opacity(0.34), radius: 24, y: 16)
+ .shadow(color: .black.opacity(0.18), radius: 8, y: 3)
+ )
+ .onExitCommand(perform: onCancel)
+ }
+}
+
+private struct UsageTipsCloseReminderSettingsPreview: View {
+ let callout: String
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 10) {
+ HStack(spacing: 6) {
+ Circle().fill(Color.red.opacity(0.78)).frame(width: 10, height: 10)
+ Circle().fill(Color.gray.opacity(0.45)).frame(width: 10, height: 10)
+ Circle().fill(Color.gray.opacity(0.36)).frame(width: 10, height: 10)
+ Text(verbatim: "TagLauncher Settings")
+ .font(.system(size: 11, weight: .semibold))
+ .foregroundStyle(Color.black.opacity(0.62))
+ .padding(.leading, 6)
+ Spacer()
+ }
+
+ HStack(spacing: 18) {
+ previewTab(systemImage: "globe", title: tr("settings.language"), active: false)
+ previewTab(systemImage: "gearshape", title: tr("settings.general"), active: true)
+ previewTab(systemImage: "keyboard", title: tr("quickSearch.hotkeys"), active: false)
+ previewTab(systemImage: "tag", title: tr("settings.tags"), active: false)
+ previewTab(systemImage: "externaldrive.fill", title: tr("settings.data"), active: false)
+ previewTab(systemImage: "info.circle", title: tr("settings.about"), active: false)
+ }
+ .frame(maxWidth: .infinity, alignment: .center)
+ .padding(.vertical, 8)
+
+ HStack(spacing: 12) {
+ previewCheckbox(title: tr("settings.launchAtLogin"), highlighted: false)
+ previewCheckbox(title: tr("settings.showInDock"), highlighted: false)
+ previewCheckbox(title: tr("settings.hideAppNames"), highlighted: false)
+ previewCheckbox(title: tr("settings.hideUsageTips"), highlighted: true)
+ }
+ .padding(10)
+ .background(
+ RoundedRectangle(cornerRadius: 10, style: .continuous)
+ .fill(Color.white.opacity(0.84))
+ )
+
+ HStack(spacing: 8) {
+ Image(systemName: "arrow.up.left")
+ .font(.system(size: 12, weight: .bold))
+ .foregroundStyle(Color.accentColor)
+ Text(callout)
+ .font(.system(size: 11, weight: .semibold))
+ .foregroundStyle(Color.accentColor)
+ .lineLimit(2)
+ .fixedSize(horizontal: false, vertical: true)
+ }
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ }
+ .padding(14)
+ .background(
+ RoundedRectangle(cornerRadius: 14, style: .continuous)
+ .fill(
+ LinearGradient(
+ colors: [
+ Color.white.opacity(0.98),
+ Color(red: 0.93, green: 0.96, blue: 1.0).opacity(0.96)
+ ],
+ startPoint: .topLeading,
+ endPoint: .bottomTrailing
+ )
+ )
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 14, style: .continuous)
+ .stroke(Color.white.opacity(0.64), lineWidth: 1)
+ )
+ }
+
+ private func previewTab(systemImage: String, title: String, active: Bool) -> some View {
+ VStack(spacing: 4) {
+ Image(systemName: systemImage)
+ .font(.system(size: 18, weight: .semibold))
+ Text(title)
+ .font(.system(size: 9, weight: .semibold))
+ .lineLimit(1)
+ .minimumScaleFactor(0.62)
+ }
+ .foregroundStyle(active ? Color.accentColor : Color.gray.opacity(0.82))
+ .frame(width: 70, height: 48)
+ .background(
+ RoundedRectangle(cornerRadius: 9, style: .continuous)
+ .fill(active ? Color.accentColor.opacity(0.12) : Color.clear)
+ )
+ }
+
+ private func previewCheckbox(title: String, highlighted: Bool) -> some View {
+ HStack(spacing: 6) {
+ ZStack {
+ RoundedRectangle(cornerRadius: 4, style: .continuous)
+ .fill(highlighted ? Color.accentColor : Color.gray.opacity(0.24))
+ Image(systemName: "checkmark")
+ .font(.system(size: 9, weight: .bold))
+ .foregroundStyle(.white)
+ }
+ .frame(width: 16, height: 16)
+
+ Text(title)
+ .font(.system(size: 10, weight: highlighted ? .bold : .semibold))
+ .foregroundStyle(Color.black.opacity(0.72))
+ .lineLimit(1)
+ .minimumScaleFactor(0.58)
+ }
+ .padding(.horizontal, 8)
+ .frame(height: 30)
+ .background(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .fill(highlighted ? Color.accentColor.opacity(0.12) : Color.clear)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .stroke(highlighted ? Color.accentColor.opacity(0.72) : Color.clear, lineWidth: 1.5)
+ )
+ }
+}
diff --git a/src/Apptag/Info.plist b/src/Apptag/Info.plist
index 9b25745..211b643 100644
--- a/src/Apptag/Info.plist
+++ b/src/Apptag/Info.plist
@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>8.0.2</string>
+ <string>8.0.3</string>
<key>CFBundleVersion</key>
- <string>20260624.2341</string>
+ <string>20260625.1615</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
diff --git a/src/Apptag/Localization/ar-Najdi.json b/src/Apptag/Localization/ar-Najdi.json
index aecd821..1349a8f 100644
--- a/src/Apptag/Localization/ar-Najdi.json
+++ b/src/Apptag/Localization/ar-Najdi.json
@@ -239,5 +239,9 @@
"theme.purple": "بنفسجي",
"theme.green": "أخضر",
"theme.blue": "أزرق",
- "theme.colorful": "ملوّن"
+ "theme.colorful": "ملوّن",
+ "usageTips.closeReminder.title": "إظهار التلميحات بعدين",
+ "usageTips.closeReminder.message": "تقدر ترجع تعرض التلميحات من الإعدادات > عام بإلغاء تحديد “إخفاء نصائح الاستخدام”.",
+ "usageTips.closeReminder.doNotRemind": "تم. لا تذكرني مرة ثانية إذا سكرت التلميحات.",
+ "usageTips.closeReminder.previewCallout": "شِلّ التحديد هنا عشان ترجع التلميحات"
}
diff --git a/src/Apptag/Localization/ar.json b/src/Apptag/Localization/ar.json
index ed8b24e..2acbf59 100644
--- a/src/Apptag/Localization/ar.json
+++ b/src/Apptag/Localization/ar.json
@@ -239,5 +239,9 @@
"theme.purple": "بنفسجي",
"theme.green": "أخضر",
"theme.blue": "أزرق",
- "theme.colorful": "ملوّن"
+ "theme.colorful": "ملوّن",
+ "usageTips.closeReminder.title": "إظهار التلميحات لاحقًا",
+ "usageTips.closeReminder.message": "يمكنك إظهار التلميحات مرة أخرى من الإعدادات > عام عبر إلغاء تحديد “إخفاء نصائح الاستخدام”.",
+ "usageTips.closeReminder.doNotRemind": "فهمت. لا تذكرني مرة أخرى عند إغلاق التلميحات.",
+ "usageTips.closeReminder.previewCallout": "ألغِ تحديد هذا الخيار لإظهار التلميحات مجددًا"
}
diff --git a/src/Apptag/Localization/cs.json b/src/Apptag/Localization/cs.json
index 373c3f7..86bfe82 100644
--- a/src/Apptag/Localization/cs.json
+++ b/src/Apptag/Localization/cs.json
@@ -239,5 +239,9 @@
"theme.purple": "Fialový",
"theme.green": "Zelený",
"theme.blue": "Modrý",
- "theme.colorful": "Barevný"
+ "theme.colorful": "Barevný",
+ "usageTips.closeReminder.title": "Zobrazit tipy později znovu",
+ "usageTips.closeReminder.message": "Tipy můžete znovu zobrazit v Nastavení > Obecné zrušením volby „Skrýt tipy k používání“.",
+ "usageTips.closeReminder.doNotRemind": "Rozumím. Při příštím zavření tipů už nepřipomínat.",
+ "usageTips.closeReminder.previewCallout": "Zrušte tuto volbu a tipy se znovu zobrazí"
}
diff --git a/src/Apptag/Localization/da.json b/src/Apptag/Localization/da.json
index 5aac946..de53f4f 100644
--- a/src/Apptag/Localization/da.json
+++ b/src/Apptag/Localization/da.json
@@ -239,5 +239,9 @@
"theme.purple": "Lilla",
"theme.green": "Grøn",
"theme.blue": "Blå",
- "theme.colorful": "Farverig"
+ "theme.colorful": "Farverig",
+ "usageTips.closeReminder.title": "Vis tips igen senere",
+ "usageTips.closeReminder.message": "Du kan vise tips igen i Indstillinger > Generelt ved at fjerne markeringen af “Skjul brugstips”.",
+ "usageTips.closeReminder.doNotRemind": "Forstået. Mind mig ikke om det igen, når jeg lukker tips.",
+ "usageTips.closeReminder.previewCallout": "Fjern markeringen for at vise tips igen"
}
diff --git a/src/Apptag/Localization/de.json b/src/Apptag/Localization/de.json
index ff27951..e084e2c 100644
--- a/src/Apptag/Localization/de.json
+++ b/src/Apptag/Localization/de.json
@@ -239,5 +239,9 @@
"theme.purple": "Violett",
"theme.green": "Grün",
"theme.blue": "Blau",
- "theme.colorful": "Bunt"
+ "theme.colorful": "Bunt",
+ "usageTips.closeReminder.title": "Tipps später wieder anzeigen",
+ "usageTips.closeReminder.message": "Du kannst die Tipps später unter Einstellungen > Allgemein wieder anzeigen, indem du „Nutzungstipps ausblenden“ deaktivierst.",
+ "usageTips.closeReminder.doNotRemind": "Verstanden. Beim Schließen der Tipps nicht mehr erinnern.",
+ "usageTips.closeReminder.previewCallout": "Deaktiviere diese Option, um Tipps wieder anzuzeigen"
}
diff --git a/src/Apptag/Localization/en.json b/src/Apptag/Localization/en.json
index b9e414d..285c321 100644
--- a/src/Apptag/Localization/en.json
+++ b/src/Apptag/Localization/en.json
@@ -239,5 +239,9 @@
"theme.purple": "Purple",
"theme.green": "Green",
"theme.blue": "Blue",
- "theme.colorful": "Colorful"
+ "theme.colorful": "Colorful",
+ "usageTips.closeReminder.title": "Show tips again later",
+ "usageTips.closeReminder.message": "You can show tips again later in Settings > General by clearing “Hide usage tips”.",
+ "usageTips.closeReminder.doNotRemind": "Got it. Don’t remind me next time I close tips.",
+ "usageTips.closeReminder.previewCallout": "Clear this checkbox to show tips again"
}
diff --git a/src/Apptag/Localization/es.json b/src/Apptag/Localization/es.json
index 504e016..d984e81 100644
--- a/src/Apptag/Localization/es.json
+++ b/src/Apptag/Localization/es.json
@@ -239,5 +239,9 @@
"theme.purple": "Morado",
"theme.green": "Verde",
"theme.blue": "Azul",
- "theme.colorful": "Colorido"
+ "theme.colorful": "Colorido",
+ "usageTips.closeReminder.title": "Volver a ver los consejos más tarde",
+ "usageTips.closeReminder.message": "Puedes volver a ver los consejos en Ajustes > General desmarcando “Ocultar consejos de uso”.",
+ "usageTips.closeReminder.doNotRemind": "Entendido. No volver a recordármelo al cerrar los consejos.",
+ "usageTips.closeReminder.previewCallout": "Desmarca esta opción para volver a verlos"
}
diff --git a/src/Apptag/Localization/fr.json b/src/Apptag/Localization/fr.json
index ce30d53..2e804b3 100644
--- a/src/Apptag/Localization/fr.json
+++ b/src/Apptag/Localization/fr.json
@@ -239,5 +239,9 @@
"theme.purple": "Violet",
"theme.green": "Vert",
"theme.blue": "Bleu",
- "theme.colorful": "Coloré"
+ "theme.colorful": "Coloré",
+ "usageTips.closeReminder.title": "Retrouver les astuces plus tard",
+ "usageTips.closeReminder.message": "Vous pourrez revoir les astuces dans Réglages > Général en décochant « Masquer les astuces d’utilisation ».",
+ "usageTips.closeReminder.doNotRemind": "Compris. Ne plus me le rappeler quand je ferme les astuces.",
+ "usageTips.closeReminder.previewCallout": "Décochez cette option pour revoir les astuces"
}
diff --git a/src/Apptag/Localization/id.json b/src/Apptag/Localization/id.json
index 83a2029..56cd360 100644
--- a/src/Apptag/Localization/id.json
+++ b/src/Apptag/Localization/id.json
@@ -239,5 +239,9 @@
"theme.purple": "Ungu",
"theme.green": "Hijau",
"theme.blue": "Biru",
- "theme.colorful": "Berwarna"
+ "theme.colorful": "Berwarna",
+ "usageTips.closeReminder.title": "Tampilkan tips lagi nanti",
+ "usageTips.closeReminder.message": "Kamu dapat menampilkan tips lagi di Pengaturan > Umum dengan menghapus centang “Sembunyikan tips penggunaan”.",
+ "usageTips.closeReminder.doNotRemind": "Mengerti. Jangan ingatkan lagi saat saya menutup tips.",
+ "usageTips.closeReminder.previewCallout": "Hapus centang ini untuk menampilkan tips lagi"
}
diff --git a/src/Apptag/Localization/it.json b/src/Apptag/Localization/it.json
index 4aec5ab..42edf02 100644
--- a/src/Apptag/Localization/it.json
+++ b/src/Apptag/Localization/it.json
@@ -239,5 +239,9 @@
"theme.purple": "Viola",
"theme.green": "Verde",
"theme.blue": "Blu",
- "theme.colorful": "Colorato"
+ "theme.colorful": "Colorato",
+ "usageTips.closeReminder.title": "Rivedere i suggerimenti più tardi",
+ "usageTips.closeReminder.message": "Puoi mostrarli di nuovo in Impostazioni > Generali disattivando “Nascondi suggerimenti d’uso”.",
+ "usageTips.closeReminder.doNotRemind": "Ho capito. Non ricordarmelo più quando chiudo i suggerimenti.",
+ "usageTips.closeReminder.previewCallout": "Disattiva questa opzione per rivedere i suggerimenti"
}
diff --git a/src/Apptag/Localization/ja.json b/src/Apptag/Localization/ja.json
index 6fdd118..f28e098 100644
--- a/src/Apptag/Localization/ja.json
+++ b/src/Apptag/Localization/ja.json
@@ -239,5 +239,9 @@
"theme.purple": "パープル",
"theme.green": "グリーン",
"theme.blue": "ブルー",
- "theme.colorful": "カラフル"
+ "theme.colorful": "カラフル",
+ "usageTips.closeReminder.title": "あとでヒントをもう一度見る",
+ "usageTips.closeReminder.message": "設定 > 一般で「使用ヒントを隠す」のチェックを外すと、ヒントを再表示できます。",
+ "usageTips.closeReminder.doNotRemind": "了解しました。次回ヒントを閉じるときは表示しません。",
+ "usageTips.closeReminder.previewCallout": "再表示するにはこのチェックを外します"
}
diff --git a/src/Apptag/Localization/ko.json b/src/Apptag/Localization/ko.json
index 61b14b8..73be18b 100644
--- a/src/Apptag/Localization/ko.json
+++ b/src/Apptag/Localization/ko.json
@@ -239,5 +239,9 @@
"theme.purple": "보라",
"theme.green": "초록",
"theme.blue": "파랑",
- "theme.colorful": "컬러풀"
+ "theme.colorful": "컬러풀",
+ "usageTips.closeReminder.title": "나중에 팁 다시 보기",
+ "usageTips.closeReminder.message": "설정 > 일반에서 “사용 팁 숨기기”를 해제하면 팁을 다시 볼 수 있습니다.",
+ "usageTips.closeReminder.doNotRemind": "알겠습니다. 다음에 팁을 닫을 때 다시 알리지 마세요.",
+ "usageTips.closeReminder.previewCallout": "팁을 다시 보려면 이 선택을 해제하세요"
}
diff --git a/src/Apptag/Localization/ms.json b/src/Apptag/Localization/ms.json
index a678e40..756c3f4 100644
--- a/src/Apptag/Localization/ms.json
+++ b/src/Apptag/Localization/ms.json
@@ -239,5 +239,9 @@
"theme.purple": "Ungu",
"theme.green": "Hijau",
"theme.blue": "Biru",
- "theme.colorful": "Berwarna-warni"
+ "theme.colorful": "Berwarna-warni",
+ "usageTips.closeReminder.title": "Tunjukkan petua lagi kemudian",
+ "usageTips.closeReminder.message": "Anda boleh menunjukkan petua semula dalam Tetapan > Umum dengan menyahtanda “Sembunyikan petua penggunaan”.",
+ "usageTips.closeReminder.doNotRemind": "Faham. Jangan ingatkan lagi apabila saya menutup petua.",
+ "usageTips.closeReminder.previewCallout": "Nyahtanda pilihan ini untuk menunjukkan petua semula"
}
diff --git a/src/Apptag/Localization/nb.json b/src/Apptag/Localization/nb.json
index ec86d9e..0dbe450 100644
--- a/src/Apptag/Localization/nb.json
+++ b/src/Apptag/Localization/nb.json
@@ -239,5 +239,9 @@
"theme.purple": "Lilla",
"theme.green": "Grønn",
"theme.blue": "Blå",
- "theme.colorful": "Fargerik"
+ "theme.colorful": "Fargerik",
+ "usageTips.closeReminder.title": "Vis tips igjen senere",
+ "usageTips.closeReminder.message": "Du kan vise tips igjen i Innstillinger > Generelt ved å fjerne avmerkingen for «Skjul brukstips».",
+ "usageTips.closeReminder.doNotRemind": "Skjønner. Ikke minn meg på dette neste gang jeg lukker tips.",
+ "usageTips.closeReminder.previewCallout": "Fjern denne avmerkingen for å vise tips igjen"
}
diff --git a/src/Apptag/Localization/nl.json b/src/Apptag/Localization/nl.json
index 7ed9eda..ee38fce 100644
--- a/src/Apptag/Localization/nl.json
+++ b/src/Apptag/Localization/nl.json
@@ -239,5 +239,9 @@
"theme.purple": "Paars",
"theme.green": "Groen",
"theme.blue": "Blauw",
- "theme.colorful": "Kleurrijk"
+ "theme.colorful": "Kleurrijk",
+ "usageTips.closeReminder.title": "Tips later opnieuw tonen",
+ "usageTips.closeReminder.message": "Je kunt tips opnieuw tonen via Instellingen > Algemeen door “Gebruikstips verbergen” uit te schakelen.",
+ "usageTips.closeReminder.doNotRemind": "Begrepen. Niet meer herinneren wanneer ik tips sluit.",
+ "usageTips.closeReminder.previewCallout": "Schakel dit uit om tips opnieuw te tonen"
}
diff --git a/src/Apptag/Localization/nn.json b/src/Apptag/Localization/nn.json
index acd04e1..aac2712 100644
--- a/src/Apptag/Localization/nn.json
+++ b/src/Apptag/Localization/nn.json
@@ -239,5 +239,9 @@
"theme.purple": "Lilla",
"theme.green": "Grøn",
"theme.blue": "Blå",
- "theme.colorful": "Fargerik"
+ "theme.colorful": "Fargerik",
+ "usageTips.closeReminder.title": "Vis tips att seinare",
+ "usageTips.closeReminder.message": "Du kan vise tips att i Innstillingar > Generelt ved å fjerne avmerkinga for «Skjul brukstips».",
+ "usageTips.closeReminder.doNotRemind": "Greitt. Ikkje minn meg på dette neste gong eg lukkar tips.",
+ "usageTips.closeReminder.previewCallout": "Fjern denne avmerkinga for å vise tips att"
}
diff --git a/src/Apptag/Localization/no.json b/src/Apptag/Localization/no.json
index ec86d9e..0dbe450 100644
--- a/src/Apptag/Localization/no.json
+++ b/src/Apptag/Localization/no.json
@@ -239,5 +239,9 @@
"theme.purple": "Lilla",
"theme.green": "Grønn",
"theme.blue": "Blå",
- "theme.colorful": "Fargerik"
+ "theme.colorful": "Fargerik",
+ "usageTips.closeReminder.title": "Vis tips igjen senere",
+ "usageTips.closeReminder.message": "Du kan vise tips igjen i Innstillinger > Generelt ved å fjerne avmerkingen for «Skjul brukstips».",
+ "usageTips.closeReminder.doNotRemind": "Skjønner. Ikke minn meg på dette neste gang jeg lukker tips.",
+ "usageTips.closeReminder.previewCallout": "Fjern denne avmerkingen for å vise tips igjen"
}
diff --git a/src/Apptag/Localization/pl.json b/src/Apptag/Localization/pl.json
index 8404df6..9c55eaf 100644
--- a/src/Apptag/Localization/pl.json
+++ b/src/Apptag/Localization/pl.json
@@ -239,5 +239,9 @@
"theme.purple": "Fioletowy",
"theme.green": "Zielony",
"theme.blue": "Niebieski",
- "theme.colorful": "Kolorowy"
+ "theme.colorful": "Kolorowy",
+ "usageTips.closeReminder.title": "Pokaż wskazówki ponownie później",
+ "usageTips.closeReminder.message": "Możesz ponownie wyświetlić wskazówki w Ustawienia > Ogólne, odznaczając „Ukryj wskazówki użycia”.",
+ "usageTips.closeReminder.doNotRemind": "Rozumiem. Nie przypominaj ponownie, gdy zamknę wskazówki.",
+ "usageTips.closeReminder.previewCallout": "Odznacz tę opcję, aby ponownie pokazać wskazówki"
}
diff --git a/src/Apptag/Localization/pt-BR.json b/src/Apptag/Localization/pt-BR.json
index 0c357fd..90e71de 100644
--- a/src/Apptag/Localization/pt-BR.json
+++ b/src/Apptag/Localization/pt-BR.json
@@ -239,5 +239,9 @@
"theme.purple": "Roxo",
"theme.green": "Verde",
"theme.blue": "Azul",
- "theme.colorful": "Colorido"
+ "theme.colorful": "Colorido",
+ "usageTips.closeReminder.title": "Ver dicas novamente depois",
+ "usageTips.closeReminder.message": "Você pode mostrar as dicas novamente em Ajustes > Geral desmarcando “Ocultar dicas de uso”.",
+ "usageTips.closeReminder.doNotRemind": "Entendi. Não lembrar de novo quando eu fechar as dicas.",
+ "usageTips.closeReminder.previewCallout": "Desmarque esta opção para ver as dicas novamente"
}
diff --git a/src/Apptag/Localization/ro.json b/src/Apptag/Localization/ro.json
index ef254b1..299fc22 100644
--- a/src/Apptag/Localization/ro.json
+++ b/src/Apptag/Localization/ro.json
@@ -239,5 +239,9 @@
"theme.purple": "Mov",
"theme.green": "Verde",
"theme.blue": "Albastru",
- "theme.colorful": "Colorată"
+ "theme.colorful": "Colorată",
+ "usageTips.closeReminder.title": "Afișează sfaturile mai târziu",
+ "usageTips.closeReminder.message": "Poți afișa din nou sfaturile în Setări > General debifând „Ascunde sfaturile de utilizare”.",
+ "usageTips.closeReminder.doNotRemind": "Am înțeles. Nu-mi mai aminti când închid sfaturile.",
+ "usageTips.closeReminder.previewCallout": "Debifează această opțiune pentru a afișa sfaturile"
}
diff --git a/src/Apptag/Localization/ru.json b/src/Apptag/Localization/ru.json
index 60577a4..2a5a2b8 100644
--- a/src/Apptag/Localization/ru.json
+++ b/src/Apptag/Localization/ru.json
@@ -239,5 +239,9 @@
"theme.purple": "Фиолетовый",
"theme.green": "Зелёный",
"theme.blue": "Синий",
- "theme.colorful": "Цветной"
+ "theme.colorful": "Цветной",
+ "usageTips.closeReminder.title": "Показать советы позже",
+ "usageTips.closeReminder.message": "Советы можно снова включить в Настройки > Общие, сняв флажок «Скрывать советы по использованию».",
+ "usageTips.closeReminder.doNotRemind": "Понятно. Больше не напоминать при закрытии советов.",
+ "usageTips.closeReminder.previewCallout": "Снимите этот флажок, чтобы снова показать советы"
}
diff --git a/src/Apptag/Localization/sr-Cyrl.json b/src/Apptag/Localization/sr-Cyrl.json
index dc48ed0..bf2ccaf 100644
--- a/src/Apptag/Localization/sr-Cyrl.json
+++ b/src/Apptag/Localization/sr-Cyrl.json
@@ -239,5 +239,9 @@
"theme.purple": "Љубичаста",
"theme.green": "Зелена",
"theme.blue": "Плава",
- "theme.colorful": "Шарена"
+ "theme.colorful": "Шарена",
+ "usageTips.closeReminder.title": "Поново прикажи савете касније",
+ "usageTips.closeReminder.message": "Савете можеш поново приказати у Подешавања > Опште тако што ћеш искључити „Сакриј савете за коришћење”.",
+ "usageTips.closeReminder.doNotRemind": "Разумем. Не подсећај ме следећи пут када затворим савете.",
+ "usageTips.closeReminder.previewCallout": "Искључи ову опцију да поново видиш савете"
}
diff --git a/src/Apptag/Localization/sv.json b/src/Apptag/Localization/sv.json
index f8a0ae1..d003fe5 100644
--- a/src/Apptag/Localization/sv.json
+++ b/src/Apptag/Localization/sv.json
@@ -239,5 +239,9 @@
"theme.purple": "Lila",
"theme.green": "Grön",
"theme.blue": "Blå",
- "theme.colorful": "Färgstark"
+ "theme.colorful": "Färgstark",
+ "usageTips.closeReminder.title": "Visa tips igen senare",
+ "usageTips.closeReminder.message": "Du kan visa tips igen i Inställningar > Allmänt genom att avmarkera “Dölj användningstips”.",
+ "usageTips.closeReminder.doNotRemind": "Jag förstår. Påminn mig inte igen när jag stänger tips.",
+ "usageTips.closeReminder.previewCallout": "Avmarkera detta för att visa tips igen"
}
diff --git a/src/Apptag/Localization/th.json b/src/Apptag/Localization/th.json
index d97847e..6bd7d2d 100644
--- a/src/Apptag/Localization/th.json
+++ b/src/Apptag/Localization/th.json
@@ -239,5 +239,9 @@
"theme.purple": "ม่วง",
"theme.green": "เขียว",
"theme.blue": "น้ำเงิน",
- "theme.colorful": "หลากสี"
+ "theme.colorful": "หลากสี",
+ "usageTips.closeReminder.title": "ดูเคล็ดลับอีกครั้งภายหลัง",
+ "usageTips.closeReminder.message": "คุณสามารถแสดงเคล็ดลับอีกครั้งได้ใน การตั้งค่า > ทั่วไป โดยเอาเครื่องหมายถูกออกจาก “ซ่อนเคล็ดลับการใช้งาน”",
+ "usageTips.closeReminder.doNotRemind": "เข้าใจแล้ว ไม่ต้องเตือนอีกเมื่อฉันปิดเคล็ดลับ",
+ "usageTips.closeReminder.previewCallout": "เอาเครื่องหมายถูกออกเพื่อแสดงเคล็ดลับอีกครั้ง"
}
diff --git a/src/Apptag/Localization/tr.json b/src/Apptag/Localization/tr.json
index 8e58f5f..34543bb 100644
--- a/src/Apptag/Localization/tr.json
+++ b/src/Apptag/Localization/tr.json
@@ -239,5 +239,9 @@
"theme.purple": "Mor",
"theme.green": "Yeşil",
"theme.blue": "Mavi",
- "theme.colorful": "Renkli"
+ "theme.colorful": "Renkli",
+ "usageTips.closeReminder.title": "İpuçlarını daha sonra tekrar göster",
+ "usageTips.closeReminder.message": "İpuçlarını Ayarlar > Genel bölümünde “Kullanım ipuçlarını gizle” seçimini kaldırarak tekrar gösterebilirsin.",
+ "usageTips.closeReminder.doNotRemind": "Anladım. İpuçlarını kapatırken tekrar hatırlatma.",
+ "usageTips.closeReminder.previewCallout": "İpuçlarını tekrar görmek için bu seçimi kaldır"
}
diff --git a/src/Apptag/Localization/uk.json b/src/Apptag/Localization/uk.json
index 54d9cfb..314df22 100644
--- a/src/Apptag/Localization/uk.json
+++ b/src/Apptag/Localization/uk.json
@@ -239,5 +239,9 @@
"theme.purple": "Фіолетова",
"theme.green": "Зелена",
"theme.blue": "Синя",
- "theme.colorful": "Барвиста"
+ "theme.colorful": "Барвиста",
+ "usageTips.closeReminder.title": "Показати поради пізніше",
+ "usageTips.closeReminder.message": "Поради можна знову показати в Параметри > Загальні, знявши позначку «Приховувати поради з використання».",
+ "usageTips.closeReminder.doNotRemind": "Зрозуміло. Більше не нагадувати, коли я закриваю поради.",
+ "usageTips.closeReminder.previewCallout": "Зніміть цей прапорець, щоб знову показати поради"
}
diff --git a/src/Apptag/Localization/vi.json b/src/Apptag/Localization/vi.json
index 77551f2..290dd2f 100644
--- a/src/Apptag/Localization/vi.json
+++ b/src/Apptag/Localization/vi.json
@@ -239,5 +239,9 @@
"theme.purple": "Tím",
"theme.green": "Xanh lá",
"theme.blue": "Xanh lam",
- "theme.colorful": "Nhiều màu"
+ "theme.colorful": "Nhiều màu",
+ "usageTips.closeReminder.title": "Xem lại mẹo sau",
+ "usageTips.closeReminder.message": "Bạn có thể xem lại mẹo trong Cài đặt > Chung bằng cách bỏ chọn “Ẩn mẹo sử dụng”.",
+ "usageTips.closeReminder.doNotRemind": "Đã hiểu. Đừng nhắc lại khi tôi đóng mẹo.",
+ "usageTips.closeReminder.previewCallout": "Bỏ chọn mục này để xem lại mẹo"
}
diff --git a/src/Apptag/Localization/zh-Hans.json b/src/Apptag/Localization/zh-Hans.json
index c4cc7e3..953f989 100644
--- a/src/Apptag/Localization/zh-Hans.json
+++ b/src/Apptag/Localization/zh-Hans.json
@@ -239,5 +239,9 @@
"theme.purple": "紫色",
"theme.green": "绿色",
"theme.blue": "蓝色",
- "theme.colorful": "炫彩"
+ "theme.colorful": "炫彩",
+ "usageTips.closeReminder.title": "以后还能重新查看使用技巧",
+ "usageTips.closeReminder.message": "后续可在「设置 > 通用」去掉「隐藏使用技巧」的勾选,再次查看 tips。",
+ "usageTips.closeReminder.doNotRemind": "知道了,下次关闭时不要再提醒我",
+ "usageTips.closeReminder.previewCallout": "去掉这里的勾选"
}
diff --git a/src/Apptag/Localization/zh-Hant.json b/src/Apptag/Localization/zh-Hant.json
index 316288e..a0457db 100644
--- a/src/Apptag/Localization/zh-Hant.json
+++ b/src/Apptag/Localization/zh-Hant.json
@@ -239,5 +239,9 @@
"theme.purple": "紫色",
"theme.green": "綠色",
"theme.blue": "藍色",
- "theme.colorful": "炫彩"
+ "theme.colorful": "炫彩",
+ "usageTips.closeReminder.title": "之後還能重新查看使用技巧",
+ "usageTips.closeReminder.message": "之後可在「設定 > 通用」取消勾選「隱藏使用技巧」,再次查看 tips。",
+ "usageTips.closeReminder.doNotRemind": "知道了,下次關閉時不要再提醒我",
+ "usageTips.closeReminder.previewCallout": "取消這裡的勾選"
}
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index 456de27..121ca85 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -2,7 +2,13 @@
## [Unreleased]
+## [8.0.3] — 2026-06-25
+
- App Grid 解除标签拖拽新增原生 Core Animation 预览反馈:当应用图标拖到容器外/容器之间空白区域时,拖拽浮层会进入克制的碎纸切片动效;容器内部空白不触发该预览,实际解除标签仍沿用原有放手后确认流程
+- App Grid 使用技巧关闭前新增提醒:首次关闭会提示用户可在“设置 > 通用”取消“隐藏使用技巧”后重新查看,并提供“不再提醒”选项;确认后才真正隐藏 tips
+- 使用技巧关闭提醒补齐 29 个语种文案,并新增本地化设置页预览,高亮“隐藏使用技巧”开关
+- 更新 `Scripts/usage_tips_qa.sh` 并新增 `Scripts/usage_tips_close_reminder_qa.sh`,覆盖关闭提醒链路、modal 防穿透和 29 语种文案完整性
+- 版本号更新为 `8.0.3`,Build 更新为 `20260625.1615`
## [8.0.2] — 2026-06-24
diff --git a/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-todo.md b/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-todo.md
new file mode 100644
index 0000000..893aa05
--- /dev/null
+++ b/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-todo.md
@@ -0,0 +1,48 @@
+# 使用技巧关闭提醒 TODO
+
+## 目标
+
+- 用户点击 App Grid 底部“使用技巧”关闭按钮时,先显示提醒弹窗。
+- 弹窗说明:后续可在“设置 > 通用”取消“隐藏使用技巧”后再次查看 tips。
+- 用户确认后才真正关闭 tips,并写入 `hideUsageTips`。
+- 用户勾选“下次关闭时不要再提醒我”后,后续关闭 tips 不再弹提醒。
+- 提醒内容和设置页预览必须覆盖 29 个语种。
+
+## 不做范围
+
+- 不改变 tips 默认展示:新用户仍默认显示。
+- 不改变 8 条 tips 的教学文案、布局和翻页逻辑。
+- 不改变设置页结构,只复用已有“隐藏使用技巧”开关。
+- 不新增 29 张静态截图资源;用运行时本地化的设置页预览组件展示同等指引信息。
+
+## TODO
+
+- [x] 新增 `skipUsageTipsCloseReminder` 默认值和持久化偏好。
+- [x] 将原生 AppKit tips 关闭按钮改为请求关闭,不再直接隐藏横幅。
+- [x] 在 `ContentView` 中新增关闭提醒弹窗状态、确认/取消处理和 modal hit-testing。
+- [x] 新增 `UsageTipsCloseReminderBubble`,复用黑底白字提醒风格。
+- [x] 新增本地化设置页预览,高亮“隐藏使用技巧”开关。
+- [x] 补齐 29 个语种的关闭提醒文案。
+- [x] 更新 `usage_tips_qa.sh`,把关闭链路检查改为提醒链路。
+- [x] 新增 `usage_tips_close_reminder_qa.sh` 专项门禁。
+- [x] 自动化 QA 与构建验证。
+- [ ] 用户体验验收后再决定是否冻结/打包。
+
+## 完成标准
+
+- 第一次点击 tips 关闭按钮时弹出提醒;取消后 tips 保持显示。
+- 确认后 tips 关闭,并与设置页“隐藏使用技巧”保持一致。
+- 勾选“不再提醒”后,后续点击关闭按钮直接隐藏 tips。
+- 弹窗出现期间不穿透 AppGrid,不触发底层应用点击、气泡或拖拽。
+- 29 个语种不缺 key、不显示裸 key,非中文语言不混入中文提醒正文。
+
+## QA 结果
+
+- `bash src/Scripts/usage_tips_qa.sh`:通过。
+- `bash src/Scripts/usage_tips_close_reminder_qa.sh`:通过。
+- `bash src/Scripts/theme_settings_qa.sh`:通过。
+- `bash src/Scripts/macos14_availability_typecheck_qa.sh`:通过。
+- `git diff --check`:通过。
+- `APP_BUILD=20260625.1615 bash src/build.sh`:通过,产物为 `src/build/TagLauncher.app`。
+- `bash src/Scripts/macos14_build_metadata_qa.sh src/build/TagLauncher.app`:通过。
+- 构建版本:`8.0.3 (20260625.1615)`。
diff --git a/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-worklog.md b/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-worklog.md
new file mode 100644
index 0000000..685ff09
--- /dev/null
+++ b/src/Docs/Requirements/2026-06-25-usage-tips-close-reminder-worklog.md
@@ -0,0 +1,12 @@
+# 使用技巧关闭提醒工作日志
+
+## 2026-06-25
+
+- 起点:`codex/dark-glass-8.0.0`,基线提交 `c8be92c Add remove tag shred preview`。
+- 需求来源:`/Users/ar/Projects/Taglauncher/Docs/X-01产品文档/2026-06-25-PRD-tip关闭提醒.MD`。
+- 产品边界:只改变关闭 tips 的提醒链路;tips 默认开启、设置页入口、教学内容和主题逻辑不变。
+- 架构决策:用已有 AppKit tips 横幅发出关闭请求,由 SwiftUI `ContentView` 统一弹 modal 和写入 `hideUsageTips`;这样可以复用现有 overlay hit-testing,避免点击穿透。
+- 视觉决策:提醒弹窗沿用黑色底部提醒/确认类弹窗风格;PRD 中“当前语言截图”用运行时本地化设置页预览实现,自动使用当前语言的设置项并高亮“隐藏使用技巧”。
+- QA 决策:更新既有 `usage_tips_qa.sh`,新增 `usage_tips_close_reminder_qa.sh`,覆盖关闭提醒链路、modal 状态、29 语种文案完整性和非中文混入中文文案风险。
+- 版本决策:用户明确后续用户可见代码改动默认应递增版本;本次从 `8.0.2 (20260624.2341)` 更新为 `8.0.3 (20260625.1615)`。
+- QA 结果:`usage_tips_qa.sh`、`usage_tips_close_reminder_qa.sh`、`theme_settings_qa.sh`、`macos14_availability_typecheck_qa.sh`、`git diff --check`、`APP_BUILD=20260625.1615 bash src/build.sh`、`macos14_build_metadata_qa.sh src/build/TagLauncher.app` 均通过。
diff --git a/src/Scripts/usage_tips_close_reminder_qa.sh b/src/Scripts/usage_tips_close_reminder_qa.sh
new file mode 100755
index 0000000..90a561c
--- /dev/null
+++ b/src/Scripts/usage_tips_close_reminder_qa.sh
@@ -0,0 +1,125 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+CONTENT_VIEW_SWIFT="$ROOT_DIR/Apptag/ContentView.swift"
+APP_GRID_SWIFT="$ROOT_DIR/Apptag/AppGridCollectionView.swift"
+EDIT_MODE_VIEWS_SWIFT="$ROOT_DIR/Apptag/EditModeViews.swift"
+APP_DEFAULTS_SWIFT="$ROOT_DIR/Apptag/AppDefaults.swift"
+LOCALIZATION_DIR="$ROOT_DIR/Apptag/Localization"
+
+fail() {
+ printf 'FAIL: %s\n' "$*" >&2
+ exit 1
+}
+
+[[ -f "$CONTENT_VIEW_SWIFT" ]] || fail "missing ContentView.swift"
+[[ -f "$APP_GRID_SWIFT" ]] || fail "missing AppGridCollectionView.swift"
+[[ -f "$EDIT_MODE_VIEWS_SWIFT" ]] || fail "missing EditModeViews.swift"
+[[ -f "$APP_DEFAULTS_SWIFT" ]] || fail "missing AppDefaults.swift"
+[[ -d "$LOCALIZATION_DIR" ]] || fail "missing Localization directory"
+
+python3 - "$CONTENT_VIEW_SWIFT" "$APP_GRID_SWIFT" "$EDIT_MODE_VIEWS_SWIFT" "$APP_DEFAULTS_SWIFT" "$LOCALIZATION_DIR" <<'PY'
+import json
+import pathlib
+import re
+import sys
+
+content_view = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8")
+app_grid = pathlib.Path(sys.argv[2]).read_text(encoding="utf-8")
+edit_mode_views = pathlib.Path(sys.argv[3]).read_text(encoding="utf-8")
+defaults = pathlib.Path(sys.argv[4]).read_text(encoding="utf-8")
+localization_dir = pathlib.Path(sys.argv[5])
+
+
+def fail(message: str) -> None:
+ print(f"FAIL: {message}", file=sys.stderr)
+ sys.exit(1)
+
+
+def require(pattern: str, source: str, message: str) -> None:
+ if not re.search(pattern, source, re.S):
+ fail(message)
+
+
+required_keys = [
+ "usageTips.closeReminder.title",
+ "usageTips.closeReminder.message",
+ "usageTips.closeReminder.doNotRemind",
+ "usageTips.closeReminder.previewCallout",
+]
+
+require(
+ r'"hideUsageTips"\s*:\s*hideUsageTips',
+ defaults,
+ "hideUsageTips must remain registered in AppDefaults",
+)
+require(
+ r'"skipUsageTipsCloseReminder"\s*:\s*false',
+ defaults,
+ "close reminder suppression must default to false",
+)
+require(
+ r"onHideUsageTips\s*:\s*requestHideUsageTips",
+ content_view,
+ "AppKit close callback must route through requestHideUsageTips",
+)
+require(
+ r"private\s+func\s+requestHideUsageTips\s*\(\)\s*\{(?P<body>.*?)skipUsageTipsCloseReminder(?P<body2>.*?)usageTipsCloseReminderVisible\s*=\s*true",
+ content_view,
+ "requestHideUsageTips must show the reminder unless suppressed",
+)
+require(
+ r"private\s+func\s+confirmUsageTipsCloseReminder\s*\(\)\s*\{(?P<body>.*?)skipUsageTipsCloseReminder\s*=\s*true(?P<body2>.*?)hideUsageTips\s*=\s*true",
+ content_view,
+ "confirmUsageTipsCloseReminder must close tips and persist suppression only after opt-in",
+)
+require(
+ r"appBubbleDisabled(?P<body>.*?)usageTipsCloseReminderVisible",
+ content_view,
+ "reminder visibility must disable app bubbles to avoid click-through",
+)
+require(
+ r"publishModalInteractionState(?P<body>.*?)usageTipsCloseReminderVisible",
+ content_view,
+ "reminder visibility must be published as modal state",
+)
+require(
+ r"private\s+func\s+requestHideUsageTips\s*\(\)\s*\{(?P<body>.*?)(?!isHidden\s*=\s*true)",
+ app_grid,
+ "native requestHideUsageTips must not hide the banner before confirmation",
+)
+if re.search(r"private\s+func\s+hideUsageTips\s*\(\)", app_grid):
+ fail("AppGridUsageTipsNSView must not keep a direct hideUsageTips handler")
+require(
+ r"struct\s+UsageTipsCloseReminderBubble\s*:\s*View",
+ edit_mode_views,
+ "missing UsageTipsCloseReminderBubble",
+)
+require(
+ r"UsageTipsCloseReminderSettingsPreview\s*\(\s*callout\s*:\s*previewCallout\s*\)",
+ edit_mode_views,
+ "reminder bubble must include the settings preview",
+)
+require(
+ r"settings\.hideUsageTips",
+ edit_mode_views,
+ "settings preview must highlight the localized Hide usage tips control",
+)
+
+files = sorted(localization_dir.glob("*.json"))
+if len(files) != 29:
+ fail(f"expected 29 localization files, found {len(files)}")
+for path in files:
+ data = json.loads(path.read_text(encoding="utf-8"))
+ missing = [key for key in required_keys if not str(data.get(key, "")).strip()]
+ if missing:
+ fail(f"{path.name} missing keys: {', '.join(missing)}")
+ if path.stem not in {"zh-Hans", "zh-Hant"}:
+ for key in required_keys:
+ value = str(data[key])
+ if "后续可在" in value or "後續可在" in value or "隐藏使用技巧" in value or "隱藏使用技巧" in value:
+ fail(f"{path.name} appears to contain copied Chinese reminder text for {key}")
+
+print("PASS: usage tips close reminder QA")
+PY
diff --git a/src/Scripts/usage_tips_qa.sh b/src/Scripts/usage_tips_qa.sh
index 25008cb..be9e0d4 100755
--- a/src/Scripts/usage_tips_qa.sh
+++ b/src/Scripts/usage_tips_qa.sh
@@ -8,6 +8,7 @@
PREFERENCES_VIEW_SWIFT="$ROOT_DIR/Apptag/PreferencesView.swift"
APP_DEFAULTS_SWIFT="$ROOT_DIR/Apptag/AppDefaults.swift"
APP_GRID_THEME_SWIFT="$ROOT_DIR/Apptag/AppGridTheme.swift"
+EDIT_MODE_VIEWS_SWIFT="$ROOT_DIR/Apptag/EditModeViews.swift"
LOCALIZATION_DIR="$ROOT_DIR/Apptag/Localization"
fail() {
@@ -21,9 +22,10 @@
[[ -f "$PREFERENCES_VIEW_SWIFT" ]] || fail "missing PreferencesView.swift"
[[ -f "$APP_DEFAULTS_SWIFT" ]] || fail "missing AppDefaults.swift"
[[ -f "$APP_GRID_THEME_SWIFT" ]] || fail "missing AppGridTheme.swift"
+[[ -f "$EDIT_MODE_VIEWS_SWIFT" ]] || fail "missing EditModeViews.swift"
[[ -d "$LOCALIZATION_DIR" ]] || fail "missing Localization directory"
-python3 - "$CONTENT_VIEW_SWIFT" "$APP_GRID_SWIFT" "$APPTAG_APP_SWIFT" "$PREFERENCES_VIEW_SWIFT" "$APP_DEFAULTS_SWIFT" "$APP_GRID_THEME_SWIFT" "$LOCALIZATION_DIR" <<'PY'
+python3 - "$CONTENT_VIEW_SWIFT" "$APP_GRID_SWIFT" "$APPTAG_APP_SWIFT" "$PREFERENCES_VIEW_SWIFT" "$APP_DEFAULTS_SWIFT" "$APP_GRID_THEME_SWIFT" "$EDIT_MODE_VIEWS_SWIFT" "$LOCALIZATION_DIR" <<'PY'
import json
import pathlib
import re
@@ -35,7 +37,8 @@
preferences = pathlib.Path(sys.argv[4]).read_text(encoding="utf-8")
defaults = pathlib.Path(sys.argv[5]).read_text(encoding="utf-8")
theme = pathlib.Path(sys.argv[6]).read_text(encoding="utf-8")
-localization_dir = pathlib.Path(sys.argv[7])
+edit_mode_views = pathlib.Path(sys.argv[7]).read_text(encoding="utf-8")
+localization_dir = pathlib.Path(sys.argv[8])
def fail(message: str) -> None:
@@ -54,6 +57,10 @@
"usageTips.previous",
"usageTips.next",
"usageTips.close",
+ "usageTips.closeReminder.title",
+ "usageTips.closeReminder.message",
+ "usageTips.closeReminder.doNotRemind",
+ "usageTips.closeReminder.previewCallout",
]
for index in range(1, 9):
required_keys.append(f"usageTips.tip{index}.title")
@@ -68,6 +75,11 @@
r'"hideUsageTips"\s*:\s*hideUsageTips',
defaults,
"AppDefaults.register must register hideUsageTips",
+)
+require(
+ r'"skipUsageTipsCloseReminder"\s*:\s*false',
+ defaults,
+ "AppDefaults.register must default the usage tips close reminder to visible",
)
for source_name, source in {
"ContentView.swift": content_view,
@@ -114,9 +126,29 @@
"ContentView must bind usage tip selection to AppGrid",
)
require(
- r"onHideUsageTips\s*:\s*\{\s*hideUsageTips\s*=\s*true\s*\}",
+ r"onHideUsageTips\s*:\s*requestHideUsageTips",
content_view,
- "closing the native usage tips banner must persist hideUsageTips",
+ "closing the native usage tips banner must route through the reminder flow",
+)
+require(
+ r'@AppStorage\("skipUsageTipsCloseReminder"\)\s+private\s+var\s+skipUsageTipsCloseReminder\s*=\s*false',
+ content_view,
+ "ContentView must persist the usage tips close reminder suppression flag",
+)
+require(
+ r"private\s+func\s+requestHideUsageTips\s*\(\)\s*\{(?P<body>.*?)skipUsageTipsCloseReminder(?P<body2>.*?)usageTipsCloseReminderVisible\s*=\s*true",
+ content_view,
+ "requestHideUsageTips must show the reminder unless the user suppressed it",
+)
+require(
+ r"private\s+func\s+confirmUsageTipsCloseReminder\s*\(\)\s*\{(?P<body>.*?)skipUsageTipsCloseReminder\s*=\s*true(?P<body2>.*?)hideUsageTips\s*=\s*true",
+ content_view,
+ "confirmUsageTipsCloseReminder must persist suppression only after opt-in and then hide tips",
+)
+require(
+ r"usageTipsCloseReminderVisible",
+ content_view,
+ "usage tips close reminder must participate in modal state and hit testing",
)
require(
r"let\s+onHideUsageTips\s*:\s*\(\)\s*->\s*Void",
@@ -124,15 +156,30 @@
"AppGridCollectionView must accept an onHideUsageTips callback",
)
require(
- r"closeButton\.action\s*=\s*\{\s*\[weak\s+self\]\s+in\s+self\?\.hideUsageTips\(\)\s*\}",
+ r"closeButton\.action\s*=\s*\{\s*\[weak\s+self\]\s+in\s+self\?\.requestHideUsageTips\(\)\s*\}",
app_grid,
- "close button must call the native hideUsageTips handler",
+ "close button must call the native requestHideUsageTips handler",
)
require(
r"coordinator\?\.onHideUsageTips\(\)",
app_grid,
"native hideUsageTips must call back to persisted SwiftUI state",
)
+require(
+ r"struct\s+UsageTipsCloseReminderBubble\s*:\s*View",
+ edit_mode_views,
+ "EditModeViews must provide the close reminder bubble",
+)
+require(
+ r"UsageTipsCloseReminderSettingsPreview\s*\(\s*callout\s*:\s*previewCallout\s*\)",
+ edit_mode_views,
+ "close reminder must pass the localized preview callout into the settings preview",
+)
+require(
+ r"struct\s+UsageTipsCloseReminderSettingsPreview\s*:\s*View(?P<body>.*?)settings\.hideUsageTips",
+ edit_mode_views,
+ "close reminder must include a localized Settings > General preview highlighting Hide usage tips",
+)
require(
r"private\s+let\s+titlePanelView\s*=\s*NSView\(\)",
diff --git a/src/TODO.md b/src/TODO.md
index 5ad408e..ed820ec 100644
--- a/src/TODO.md
+++ b/src/TODO.md
@@ -30,6 +30,14 @@
## Done
+- [2026-06-25] 使用技巧关闭提醒。
+ - 结果: 点击 App Grid 使用技巧关闭按钮时先弹出提醒,说明可在“设置 > 通用”取消“隐藏使用技巧”重新查看;用户确认后才隐藏 tips,勾选“不再提醒”后后续关闭直接隐藏。
+ - 范围: 关闭按钮链路、提醒弹窗、本地化设置页预览、`skipUsageTipsCloseReminder` 偏好、29 语种提醒文案、专项 QA。
+ - 不改范围: tips 默认仍显示;8 条教学内容、主题/拖拽/排序/Quick Search 功能不变。
+ - 验证: `usage_tips_qa.sh`、`usage_tips_close_reminder_qa.sh`、`theme_settings_qa.sh`、`macos14_availability_typecheck_qa.sh`、`git diff --check`、`APP_BUILD=20260625.1615 bash src/build.sh`、`macos14_build_metadata_qa.sh src/build/TagLauncher.app` 通过。
+ - 版本: `8.0.3 (20260625.1615)`。
+ - 文档: `Docs/Requirements/2026-06-25-usage-tips-close-reminder-todo.md`、`Docs/Requirements/2026-06-25-usage-tips-close-reminder-worklog.md`。
+
- [2026-06-25] App Grid 解除标签碎纸预览动效。
- 结果: 拖动应用到容器外/容器之间空白区域时,拖拽浮层进入原生 Core Animation 碎纸切片预览;离开该区域恢复正常。
- 范围: 只改拖拽浮层视觉反馈和空白 drop 预览资格判断;不改标签数据、不改容器内空白语义、不改放手后的解除标签确认流程。
--
Gitblit v1.9.3