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