Browse Source

新增scheme解析,新增多语言,新增ext,调整风格本地模型,新增styleid

kailen 3 weeks ago
parent
commit
c2ffee6794

BIN
.DS_Store


+ 20 - 0
AIEmoji.xcodeproj/project.pbxproj

@@ -7,6 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		605E20552DCC90CE0069F4B6 /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 605E20542DCC90CE0069F4B6 /* SchemeHandler.swift */; };
+		605E20592DCCAF3C0069F4B6 /* SchemeHandler+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 605E20582DCCAF3C0069F4B6 /* SchemeHandler+Ext.swift */; };
+		605E205B2DCCB8D20069F4B6 /* TSCustomAlertController+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 605E205A2DCCB8D20069F4B6 /* TSCustomAlertController+Ext.swift */; };
 		A80327B02D813A0200AF7878 /* TSTTPSelectStyleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80327AF2D8139FC00AF7878 /* TSTTPSelectStyleCell.swift */; };
 		A80327B32D813D4900AF7878 /* TSTTPInputVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80327B22D813D4800AF7878 /* TSTTPInputVC.swift */; };
 		A80327B62D813D8700AF7878 /* TSTTPInputVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80327B52D813D8100AF7878 /* TSTTPInputVM.swift */; };
@@ -254,6 +257,9 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		605E20542DCC90CE0069F4B6 /* SchemeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemeHandler.swift; sourceTree = "<group>"; };
+		605E20582DCCAF3C0069F4B6 /* SchemeHandler+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SchemeHandler+Ext.swift"; sourceTree = "<group>"; };
+		605E205A2DCCB8D20069F4B6 /* TSCustomAlertController+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSCustomAlertController+Ext.swift"; sourceTree = "<group>"; };
 		6E77A292B548CD79E381757E /* Pods-AIEmoji.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AIEmoji.release.xcconfig"; path = "Target Support Files/Pods-AIEmoji/Pods-AIEmoji.release.xcconfig"; sourceTree = "<group>"; };
 		86FB4D6AEFDDA7A2017F307C /* Pods_AIEmoji.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AIEmoji.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		A80327AF2D8139FC00AF7878 /* TSTTPSelectStyleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTTPSelectStyleCell.swift; sourceTree = "<group>"; };
@@ -555,6 +561,15 @@
 			path = Pods;
 			sourceTree = "<group>";
 		};
+		605E20532DCC90BE0069F4B6 /* SchemeHandler */ = {
+			isa = PBXGroup;
+			children = (
+				605E20542DCC90CE0069F4B6 /* SchemeHandler.swift */,
+				605E20582DCCAF3C0069F4B6 /* SchemeHandler+Ext.swift */,
+			);
+			path = SchemeHandler;
+			sourceTree = "<group>";
+		};
 		97C7E956DF0731529C52523E /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -1193,6 +1208,7 @@
 			isa = PBXGroup;
 			children = (
 				A83404C72D9BEC0700C140E4 /* UIFont+TSEx.swift */,
+				605E205A2DCCB8D20069F4B6 /* TSCustomAlertController+Ext.swift */,
 			);
 			path = Ex;
 			sourceTree = "<group>";
@@ -1693,6 +1709,7 @@
 		A8F774D82D38EA8C00AA6E93 /* Common */ = {
 			isa = PBXGroup;
 			children = (
+				605E20532DCC90BE0069F4B6 /* SchemeHandler */,
 				A83404C62D9BEC0300C140E4 /* Ex */,
 				A89EA6AA2D5B3EE8000EB181 /* TSRealmManager */,
 				A80E73E32D533EB000C64288 /* Purchase */,
@@ -2326,6 +2343,7 @@
 				A8F776372D3A806E00AA6E93 /* TSGenmojiItemCell.swift in Sources */,
 				A89EA6692D59AA31000EB181 /* CameraInputBarAccessoryView.swift in Sources */,
 				A89EA66B2D59AA31000EB181 /* TSTextMessageContentCell.swift in Sources */,
+				605E205B2DCCB8D20069F4B6 /* TSCustomAlertController+Ext.swift in Sources */,
 				A804B98F2DC0F0F700C494C7 /* TSTTPRatioView.swift in Sources */,
 				A83404D52D9D28D700C140E4 /* TSAIPhotoBrowseVC.swift in Sources */,
 				A89EA66C2D59AA31000EB181 /* TSMessageContentCell.swift in Sources */,
@@ -2337,9 +2355,11 @@
 				A8BA76472DA4CC70000B6707 /* TSPTPSelectStyleView.swift in Sources */,
 				A8F7750A2D38EA8C00AA6E93 /* TSNetworkTool.swift in Sources */,
 				A8F7762D2D3A74A100AA6E93 /* TSGenmojiGennerateCell.swift in Sources */,
+				605E20552DCC90CE0069F4B6 /* SchemeHandler.swift in Sources */,
 				A89EA6BC2D5DFB12000EB181 /* TSViewController.swift in Sources */,
 				A85E479D2D6809DC0018D62D /* TSBigIconBrowseCell.swift in Sources */,
 				A89EA6832D59F4F9000EB181 /* TSChatViewController+ChatDelegate.swift in Sources */,
+				605E20592DCCAF3C0069F4B6 /* SchemeHandler+Ext.swift in Sources */,
 				A89EA6A32D5B26E3000EB181 /* TSDBAIChatList.swift in Sources */,
 				A8F7753F2D39340E00AA6E93 /* TSSetingVC.swift in Sources */,
 				A8F7762B2D3A70B200AA6E93 /* PaddedLabel.swift in Sources */,

+ 25 - 30
AIEmoji/AppDelegate.swift

@@ -9,10 +9,9 @@ import UIKit
 var kAppNewVerison = ""
 @main
 class AppDelegate: UIResponder, UIApplicationDelegate {
-
     var window: UIWindow?
     var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = .invalid
-    
+
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         window = UIWindow(frame: UIScreen.main.bounds)
         window?.backgroundColor = UIColor.black
@@ -24,21 +23,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     func goToLoadVC() {
         let launchVC = TSLaunchVC()
-        launchVC.dismissHandler = { [weak self]  in
+        launchVC.dismissHandler = { [weak self] in
             guard let self = self else { return }
             JudgmentSkipPage()
         }
         window?.rootViewController = launchVC
     }
 
-    func goToTab(){
+    func goToTab() {
         window?.rootViewController = TSTabBarController()
     }
-    
 
     func JudgmentSkipPage() {
         if AppDelegate.isFirstInstallApp() {
-            let bootPageVC  = TSBootPageVC { [weak self]  in
+            let bootPageVC = TSBootPageVC { [weak self] in
                 guard let self = self else { return }
                 UserDefaults.standard.set("1", forKey: "isFirstInstallApp")
                 UserDefaults.standard.synchronize()
@@ -46,65 +44,63 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
             }
             let navi = TSBaseNavigationC(rootViewController: bootPageVC)
             window?.rootViewController = navi
-        }else{
+        } else {
             goToTab()
         }
     }
-    
 
     func initPlatform() {
         TSCrashReporterTool.shared.setup()
         TSCrashReporterTool.shared.printCrashReports()
         TSColorConfigShared.naviMianTextColor = .white
         checkAppConfig()
-        
+
         kHandleDBHistoryOperation()
     }
-
 }
 
 extension AppDelegate {
-
     func applicationWillTerminate(_ application: UIApplication) {
         // 当应用即将被终止时,这里也可以添加数据保存逻辑,但系统留给的时间很有限
         NotificationCenter.default.post(name: .kApplicationWillTerminate, object: nil)
     }
-    
+
     func applicationDidEnterBackground(_ application: UIApplication) {
         beginBackgroundTask()
 //        ///添加测试数据
 //        let userDefaults = UserDefaults.standard
 //        let lastGreetingDateString = userDefaults.string(forKey: "kEveryDayPopPurchase")
 //        userDefaults.set(String(Int(lastGreetingDateString!)!+1), forKey: "kEveryDayPopPurchase")//测试用的
-
     }
-    
+
     func beginBackgroundTask() {
         backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask { [weak self] in
             self?.endBackgroundTask()
         }
     }
-    
+
     func endBackgroundTask() {
         if backgroundTaskIdentifier != .invalid {
             UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
             backgroundTaskIdentifier = .invalid
         }
     }
-    
+
     func applicationWillEnterForeground(_ application: UIApplication) {
 //        handleShowEveryDayPopPurchase()
         checkAppConfig()
     }
-}
 
+    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
+        return SchemeHandler.shared.handle(url: url)
+    }
+}
 
 extension AppDelegate {
-    
-    static func isFirstInstallApp() -> Bool{
+    static func isFirstInstallApp() -> Bool {
         return UserDefaults.standard.string(forKey: "isFirstInstallApp") == nil
     }
-    
+
 //    func handleShowEveryDayPopPurchase() {
 //        AppDelegate.showEveryDayPopPurchase { vc in
 //            if let vc = vc ,let rootvc = self.window?.rootViewController {
@@ -114,27 +110,27 @@ extension AppDelegate {
 //        }
 //    }
 //    static func showEveryDayPopPurchase(showVCHandle:@escaping (UIViewController?)->Void) {
-//        
+//
 //        if Self.isFirstInstallApp() || kPurchaseDefault.isVip {
 //            //启动页->引导图->会员购买
 //            showVCHandle(nil)
 //            return
 //        }
-//        
+//
 //        //启动页->会员购买
 //        // 1. 获取当前日期
 //        let currentDate = Date()
 //        let dateFormatter = DateFormatter()
 //        dateFormatter.dateFormat = "yyyyMMdd"
 //        let currentDateString = dateFormatter.string(from: currentDate)
-//        
+//
 //        // 2. 获取上次弹窗的日期
 //        let userDefaults = UserDefaults.standard
 //        let lastGreetingDateString = userDefaults.string(forKey: "kEveryDayPopPurchase")
-//        
+//
 //        // 3. 检查是否需要显示弹窗 "20250319"
 //        if lastGreetingDateString != currentDateString {
-//            
+//
 //            // 4. 弹窗付费引导
 //            let vc = TSPurchaseVC()
 //            vc.closePageBlock = {
@@ -155,15 +151,14 @@ extension AppDelegate {
         let currentDateString = dateFormatter.string(from: currentDate)
         UserDefaults.standard.set(currentDateString, forKey: "kEveryDayPopPurchase")
     }
-    
-    func checkAppConfig(){
-        _ = TSNetworkShared.get(urlType: .config) { data,error in
-            
+
+    func checkAppConfig() {
+        _ = TSNetworkShared.get(urlType: .config) { data, _ in
+
             if let result = kNetWorkResultSuccess(data: data) {
                 kAppNewVerison = result.safeString(forKey: "version")
                 NotificationCenter.default.post(name: .kRefreshSettingView, object: nil)
             }
         }
     }
-    
 }

+ 1 - 1
AIEmoji/Business/TSAILIstVC/TSAIListHistoryBaseVC/TSAIListHistoryBaseCell.swift

@@ -8,7 +8,7 @@
 class TSAIListHistoryBaseCell: TSBaseCollectionCell,TSSimpleConfigurableView {
     
     weak var delegate: (any TSSmalCoacopods.TSSimpleCollectionViewDelegate)?
-    var indexPath: IndexPath = IndexPath(item: 0, section: 0)
+    var indexPath: IndexPath? = IndexPath(item: 0, section: 0)
     var data: Any? {
         didSet {
 //            debugPrint("TSAIListHistoryBaseCell data didSet")

+ 1 - 1
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiItemCell.swift

@@ -9,7 +9,7 @@
 class TSGenmojiItemCell: TSBaseCollectionCell ,TSSimpleConfigurableView {
     
     weak var delegate: (any TSSmalCoacopods.TSSimpleCollectionViewDelegate)?
-    var indexPath: IndexPath = IndexPath(item: 0, section: 0)
+    var indexPath: IndexPath? = IndexPath(item: 0, section: 0)
     var data: Any? {
         didSet {
             if let dataModel = data as? TSActionInfoModel{

+ 164 - 169
AIEmoji/Business/TSPTPGeneratorVC/TSPTPInputVC/TSPTPInputVC.swift

@@ -7,90 +7,87 @@
 
 import PhotosUI
 class TSPTPInputVC: TSBaseVC {
-
     lazy var viewModel: TSPTPInputVM = {
         let viewModel = TSPTPInputVM()
-        viewModel.isCanGennerateBlock = { [weak self] isCan in
+        viewModel.isCanGennerateBlock = { [weak self] _ in
             guard let self = self else { return }
             setCreatBtnEnabled()
         }
         return viewModel
     }()
-    
+
     lazy var photoPickerManager: TSPhotoPickerManager = {
         let photoPickerManager = TSPhotoPickerManager(viewController: self)
         return photoPickerManager
     }()
-    
+
     private var keyboardHeight: CGFloat = 0
     var hintBaseVC = TSAIListHintBaseVC(config: .defaultConfig)
-    
-    //###################################### 导航栏 view ######################################
+
+    // ###################################### 导航栏 view ######################################
 
     lazy var vipBtn: UIButton = {
-       let vipBtn = UIButton.createButton(image: UIImage(named: "nav_vip")) { [weak self]  in
-           guard let self = self else { return }
-           TSPurchaseVC.show(target: self) {}
-       }
-       return vipBtn
-   }()
-    
+        let vipBtn = UIButton.createButton(image: UIImage(named: "nav_vip")) { [weak self] in
+            guard let self = self else { return }
+            TSPurchaseVC.show(target: self) {}
+        }
+        return vipBtn
+    }()
 
     lazy var navBarView: TSBaseNavContentBarView = {
-       let navBarView = TSBaseNavContentBarView()
-       
-       let titleImageView = UIImageView.createImageView(imageName: "nav_title_pic",contentMode: .scaleToFill)
-       navBarView.barView.addSubview(titleImageView)
-       titleImageView.snp.makeConstraints { make in
-           make.centerY.equalToSuperview()
-           make.left.equalTo(16)
-       }
-       
-       navBarView.barView.addSubview(vipBtn)
-       vipBtn.snp.makeConstraints { make in
-           make.centerY.equalToSuperview()
-           make.trailing.equalTo(-16)//(-60)
-           make.width.height.equalTo(24)
-       }
-       
-       return navBarView
-   }()
-    
-    //###################################### cusStackView ######################################
+        let navBarView = TSBaseNavContentBarView()
+
+        let titleImageView = UIImageView.createImageView(imageName: "nav_title_pic", contentMode: .scaleToFill)
+        navBarView.barView.addSubview(titleImageView)
+        titleImageView.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.left.equalTo(16)
+        }
+
+        navBarView.barView.addSubview(vipBtn)
+        vipBtn.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-16) // (-60)
+            make.width.height.equalTo(24)
+        }
+
+        return navBarView
+    }()
+
+    // ###################################### cusStackView ######################################
     lazy var cusStackView: TSCustomStackView = {
-        let cusStackView = TSCustomStackView(axis: .vertical,spacing: 0)
+        let cusStackView = TSCustomStackView(axis: .vertical, spacing: 0)
         cusStackView.scrollView.isScrollEnabled = true
         return cusStackView
     }()
 
-    //###################################### 上传图片 ######################################
+    // ###################################### 上传图片 ######################################
     lazy var uploadView: TSPTPUploadView = {
         let uploadView = TSPTPUploadView()
         uploadView.clickHandel = { [weak self] index in
             guard let self = self else { return }
-            
-            if index == 0 {//删除
+
+            if index == 0 { // 删除
                 viewModel.upLoadImage = nil
                 uploadView.upLoadImage = nil
-            }else{//添加
-                if TSAIListHintBaseVC.isShowUploadImageHint{
+            } else { // 添加
+                if TSAIListHintBaseVC.isShowUploadImageHint {
                     TSAIListHintBaseVC.isShowUploadImageHint = false
                     presentModalHintVC()
-                }else {
+                } else {
                     pickSinglePhoto()
                 }
             }
         }
         return uploadView
     }()
-    
-    
-    func pickSinglePhoto()  {
-        photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
+
+    func pickSinglePhoto() {
+        photoPickerManager.pickCustomSinglePhoto { [weak self] image, errorString in
             guard let self = self else { return }
             if let errorString = errorString {
                 TSToastShared.showToast(text: errorString)
-            }else{
+            } else {
                 viewModel.upLoadImage = image
                 uploadView.upLoadImage = image
             }
@@ -99,13 +96,13 @@ class TSPTPInputVC: TSBaseVC {
             }
         }
     }
-    
-    //###################################### 选择风格 ######################################
+
+    // ###################################### 选择风格 ######################################
     lazy var selectStyleView: TSPTPSelectStyleView = {
         let selectStyleView = TSPTPSelectStyleView()
         selectStyleView.currentIndexPath = IndexPath(item: viewModel.selectedStyleIndex, section: 0)
         selectStyleView.dataArray = viewModel.ptpStyleModels
-        selectStyleView.clickHandle = { [weak self] indexPath ,model in
+        selectStyleView.clickHandle = { [weak self] indexPath, model in
             guard let self = self else { return }
             viewModel.selectedPTPStyleModel = model
             viewModel.selectedStyleIndex = indexPath.item
@@ -114,9 +111,8 @@ class TSPTPInputVC: TSBaseVC {
         }
         return selectStyleView
     }()
-    
-    
-    //###################################### 输入框 ######################################
+
+    // ###################################### 输入框 ######################################
     lazy var customTextView: TSPlaceholderTextView = {
         let customTextView = TSPlaceholderTextView(
             placeholder: "Describe how you want to transform".localized,
@@ -129,13 +125,12 @@ class TSPTPInputVC: TSBaseVC {
         customTextView.returnKeyType = .done
         return customTextView
     }()
-    
+
     lazy var clearBtn: TSUIExpandedTouchButton = {
         let clearBtn = TSUIExpandedTouchButton()
         clearBtn.setUpButton(
             image: UIImage(named: "clear_text")
-        )
-        { [weak self]  in
+        ) { [weak self] in
             guard let self = self else { return }
             customTextView.text = ""
             textViewDidChange(customTextView)
@@ -143,9 +138,9 @@ class TSPTPInputVC: TSBaseVC {
         clearBtn.isHidden = true
         return clearBtn
     }()
-    
-    var promptTextViewH:CGFloat = 96.0
-    lazy var promptTextView:UIView =  {
+
+    var promptTextViewH: CGFloat = 96.0
+    lazy var promptTextView: UIView = {
         let promptTextView = UIView()
         promptTextView.isHidden = true
         promptTextView.clipsToBounds = true
@@ -156,10 +151,10 @@ class TSPTPInputVC: TSBaseVC {
         bgView.snp.makeConstraints { make in
             make.edges.equalTo(UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16))
         }
-        
+
         bgView.addSubview(customTextView)
         bgView.addSubview(clearBtn)
-        
+
         customTextView.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
             make.leading.equalTo(12.0)
@@ -167,50 +162,51 @@ class TSPTPInputVC: TSBaseVC {
             make.bottom.equalTo(-12.0)
             make.trailing.equalTo(-20)
         }
-        
+
         clearBtn.snp.makeConstraints { make in
             make.width.height.equalTo(16.0)
             make.trailing.equalTo(-12)
             make.bottom.equalTo(-12.0)
         }
-        
+
         return promptTextView
     }()
-    //###################################### 集合视图 ######################################
+
+    // ###################################### 集合视图 ######################################
     private var collectionViewObserver: CollectionViewObserver!
-    let collectionViewBtootm:CGFloat = 80
+    let collectionViewBtootm: CGFloat = 80
     lazy var collectionComponent: TSCollectionViewComponent = {
         let layout = UICollectionViewFlowLayout()
         let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [:])
         cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: collectionViewBtootm, right: 0)
         cp.collectionView.isScrollEnabled = false
- 
+
         // 禁用自动 contentInset 调整
-          if #available(iOS 11.0, *) {
-              cp.collectionView.contentInsetAdjustmentBehavior = .never
-          } else {
-              automaticallyAdjustsScrollViewInsets = false
-          }
+        if #available(iOS 11.0, *) {
+            cp.collectionView.contentInsetAdjustmentBehavior = .never
+        } else {
+            automaticallyAdjustsScrollViewInsets = false
+        }
 
-        cp.sectionActionHandler = { [weak self] cellCp, indexPath in
+        cp.sectionActionHandler = { [weak self] cellCp, _ in
             guard let self = self else { return }
             if let cmd = cellCp as? String {
                 if cmd == "delete" {
-                    showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  {
+                    showCustomAlert(message: "Are you sure to delete".localized, deleteHandler: {
                         self.viewModel.removeAllHistoryList()
                         self.updataCollectionView()
                     })
-                }else if cmd == "more" {
+                } else if cmd == "more" {
                     let historyVC = TSPTPHistoryVC()
                     kPushVC(target: self, modelVC: historyVC)
                 }
             }
         }
-        
-        cp.itemActionHandler = { [weak self] (object, indexPath) in
+
+        cp.itemActionHandler = { [weak self] object, indexPath in
             guard let self = self else { return }
-            //删除过期的任务
-            if let cmd = object as? String, cmd == "delete_task_expired"  {
+            // 删除过期的任务
+            if let cmd = object as? String, cmd == "delete_task_expired" {
                 if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel,
                    let currentActionInfoModel = sections.items.safeObj(At: indexPath.item) {
                     TSRMShared.ptpDBHistory.deleteListModel(id: currentActionInfoModel.dataModel.id)
@@ -218,48 +214,46 @@ class TSPTPInputVC: TSBaseVC {
                 }
             }
         }
-        
-        cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
+
+        cp.itemDidSelectedHandler = { [weak self] _, indexPath in
             guard let self = self else { return }
             if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel,
-               let dataModel = sections.items.safeObj(At: indexPath.item)?.dataModel
-            {
-
-                var dataModelArray:[TSActionInfoModel] = []
+               let dataModel = sections.items.safeObj(At: indexPath.item)?.dataModel {
+                var dataModelArray: [TSActionInfoModel] = []
                 for itemModel in sections.items {
-                    if itemModel.dataModel.status == "success" || itemModel.dataModel.modelType == .example{
+                    if itemModel.dataModel.status == "success" || itemModel.dataModel.modelType == .example {
                         dataModelArray.append(itemModel.dataModel)
                     }
                 }
-  
+
                 let browseVC = TSAIPhotoBrowseVC()
                 browseVC.dataModelArray = dataModelArray
                 browseVC.currentIndex = dataModelArray.firstIndex(of: dataModel) ?? 0
-                kPresentModalVC(target: self, modelVC: browseVC,transitionStyle: .crossDissolve)
+                kPresentModalVC(target: self, modelVC: browseVC, transitionStyle: .crossDissolve)
             }
         }
         cp.collectionView.keyboardDismissMode = .interactive
         return cp
     }()
-    
-    //###################################### Button ######################################
-    lazy var creatBtnView:TSAppBtnView  = {
+
+    // ###################################### Button ######################################
+    lazy var creatBtnView: TSAppBtnView = {
         let creatBtnView = TSAppBtnView()
         creatBtnView.setUpButton(style: .generate, vipFreeNumType: .picToPic) { [weak self] in
             guard let self = self else { return }
             generateImage()
         }
         creatBtnView.setBtnEnabled(isEnabled: false)
-        creatBtnView.isIconVipBlock = { [weak self]  in
-            guard let self = self else { return false}
+        creatBtnView.isIconVipBlock = { [weak self] in
+            guard let self = self else { return false }
             var showVip = kPurchaseDefault.generateVipShow(type: .picToPic)
             if showVip == false {
                 showVip = self.viewModel.selectedPTPStyleModel.isVip
             }
             return showVip
         }
-        creatBtnView.isClickVipBlock = { [weak self]  in
-            guard let self = self else { return false}
+        creatBtnView.isClickVipBlock = { [weak self] in
+            guard let self = self else { return false }
             var isVip = kPurchaseDefault.freeNumAvailable(type: .picToPic) == false
             if viewModel.selectedPTPStyleModel.isVip == true {
                 isVip = true
@@ -268,19 +262,17 @@ class TSPTPInputVC: TSBaseVC {
         }
         return creatBtnView
     }()
-    
-    
+
     override func createView() {
-        
         let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickView))
         tapGesture.cancelsTouchesInView = false
         view.addGestureRecognizer(tapGesture)
-        
+
         navBarContentView.addSubview(navBarView)
         navBarView.snp.makeConstraints { make in
             make.edges.equalToSuperview()
         }
-        
+
         contentView.addSubview(cusStackView)
         cusStackView.snp.makeConstraints { make in
             make.edges.equalToSuperview()
@@ -293,89 +285,87 @@ class TSPTPInputVC: TSBaseVC {
             make.trailing.equalTo(-16)
             make.height.equalTo(48)
         }
-        
-        setUpCusStackView()
 
+        setUpCusStackView()
     }
-    
+
     override func dealThings() {
         updataCollectionView()
-        //监听 vip 变化
+        // 监听 vip 变化
         NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
         updateVipView()
-        
+
         TSAIListHintBaseVC.userDefaultsKey = "isFirstUploadImagePTP"
 
         // 监听键盘事件
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
-        
-        //监听collectionView 的 contentSize
+
+        // 监听collectionView 的 contentSize
         collectionViewObserver = CollectionViewObserver(collectionView: collectionComponent.collectionView)
-        collectionViewObserver.onContentSizeChange = {[weak self]  size in
+        collectionViewObserver.onContentSizeChange = { [weak self] size in
             guard let self = self else { return }
             print("collectionViewObserver 内容大小变化: \(size)")
             self.collectionComponent.collectionView.snp.updateConstraints { make in
-               make.height.equalTo(size.height)
+                make.height.equalTo(size.height)
             }
         }
-        
-        //监听按钮任务生成中
-        NotificationCenter.default.addObserver(forName: .kBaseOperationQueueCountChanged, object: nil, queue: nil) { [weak self] notification in
+
+        // 监听按钮任务生成中
+        NotificationCenter.default.addObserver(forName: .kBaseOperationQueueCountChanged, object: nil, queue: nil) { [weak self] _ in
             guard let self = self else { return }
             setCreatBtnEnabled()
         }
-        
-        //后台生成 UI 任务,刷新 UI界面
+
+        // 后台生成 UI 任务,刷新 UI界面
         NotificationCenter.default.addObserver(forName: .kGeneratePTPOperationChanged, object: nil, queue: nil) { [weak self] notification in
             guard let self = self else { return }
 
-            if let userInfo = notification.userInfo as? [String: Any],let state = userInfo["state"] as? TSProgressState {
+            if let userInfo = notification.userInfo as? [String: Any], let state = userInfo["state"] as? TSProgressState {
                 dePrint("TSBaseOperation stateDatauPblished 收到 = \(state)")
 //                if state.isResult {//有结果,一定要刷新
 //                    updataCollectionView()
 //                }else if self.isViewVisible == false {
-////                    dePrint("TSBaseOperation 视图不可见")
+                ////                    dePrint("TSBaseOperation 视图不可见")
 //                    return
 //                }else
 //                if state.reloadNewData {//主要是给pending用,让他再视图中有个位置占着
-                    updataCollectionView()
+                updataCollectionView()
 //                }
             }
         }
-        
-        //同时 VC主动刷新UI界面
-        NotificationCenter.default.addObserver(forName: .kPTPDataChanged, object: nil, queue: nil) { [weak self] notification in
+
+        // 同时 VC主动刷新UI界面
+        NotificationCenter.default.addObserver(forName: .kPTPDataChanged, object: nil, queue: nil) { [weak self] _ in
             guard let self = self else { return }
             DispatchQueue.main.async {
                 debugPrint("kPTPDataChanged listModels.first=\(self.viewModel.listModels.first?.toJSONString())")
                 self.updataCollectionView()
             }
         }
+
+        NotificationCenter.default.addObserver(self, selector: #selector(autoSelectImageToImageStyle(notify:)), name: .schemeImageToImageStylePick, object: nil)
     }
-    
-    func updataCollectionView(){
-        self.viewModel.updateRecentData()
+
+    func updataCollectionView() {
+        viewModel.updateRecentData()
         collectionComponent.clear()
-        collectionComponent.reloadView(with:viewModel.colDataArray)
+        collectionComponent.reloadView(with: viewModel.colDataArray)
     }
-
 }
 
 extension TSPTPInputVC {
-    
-    func presentModalHintVC(){
+    func presentModalHintVC() {
         hintBaseVC = TSAIListHintBaseVC(config: .defaultConfig) { [weak self] image in
             guard let self = self else { return }
             viewModel.upLoadImage = image
             uploadView.upLoadImage = image
             hintBaseVC.dismissPageVC()
         }
-        kPresentModalVC(target: self, modelVC: hintBaseVC,transitionStyle: .crossDissolve)
+        kPresentModalVC(target: self, modelVC: hintBaseVC, transitionStyle: .crossDissolve)
     }
-    
-    func setUpCusStackView(){
 
+    func setUpCusStackView() {
         let uploadPhotoTitleView = TSTitleView.creatTitleView(title: "Upload Photo".localized, subTitle: "")
         cusStackView.addSubviewToStack(uploadPhotoTitleView)
         uploadPhotoTitleView.snp.makeConstraints { make in
@@ -384,7 +374,7 @@ extension TSPTPInputVC {
         }
 
         let hintBtn = TSUIExpandedTouchButton()
-        hintBtn.setUpButton(image: UIImage(named: "ptp_hint")){ [weak self]  in
+        hintBtn.setUpButton(image: UIImage(named: "ptp_hint")) { [weak self] in
             guard let self = self else { return }
             presentModalHintVC()
         }
@@ -394,39 +384,39 @@ extension TSPTPInputVC {
             make.trailing.equalTo(-16)
             make.width.height.equalTo(16)
         }
- 
+
         cusStackView.addSubviewToStack(uploadView)
         uploadView.snp.makeConstraints { make in
             make.height.equalTo(uploadView.viewH)
             make.width.equalTo(k_ScreenWidth)
         }
-        
+
         let selectStyleTitleView = TSTitleMoreView()
-        selectStyleTitleView.setTitle(title: "Select Style".localized) { [weak self]  in
+        selectStyleTitleView.setTitle(title: "Select Style".localized) { [weak self] in
             guard let self = self else { return }
             let selectStyleVC = TSGennertatorSelectStyleVC()
             selectStyleVC.currentIndexPath = IndexPath(item: viewModel.selectedStyleIndex, section: 0)
             selectStyleVC.dataArray = viewModel.ptpStyleModels
-            selectStyleVC.selectedValueBlock = { [weak self] indexPath ,model in
+            selectStyleVC.selectedValueBlock = { [weak self] indexPath, _ in
                 guard let self = self else { return }
                 selectStyleView.collectionView(selectStyleView.styleCollectionView, didSelectItemAt: indexPath)
             }
-            kPresentModalVC(target: self, modelVC: selectStyleVC,transitionStyle: .coverVertical)
+            kPresentModalVC(target: self, modelVC: selectStyleVC, transitionStyle: .coverVertical)
         }
         cusStackView.addSubviewToStack(selectStyleTitleView)
         selectStyleTitleView.snp.makeConstraints { make in
             make.height.equalTo(selectStyleTitleView.viewH)
             make.width.equalTo(k_ScreenWidth)
         }
-        
+
         cusStackView.addSubviewToStack(selectStyleView)
         selectStyleView.snp.makeConstraints { make in
             make.height.equalTo(selectStyleView.viewH)
             make.width.equalTo(k_ScreenWidth)
         }
-        
-        cusStackView.addSubviewToStack(promptTextView,length: promptTextViewH)
-        
+
+        cusStackView.addSubviewToStack(promptTextView, length: promptTextViewH)
+
         promptTextView.isHidden = !viewModel.selectedPTPStyleModel.input
 
         cusStackView.addSubviewToStack(collectionComponent.collectionView)
@@ -434,95 +424,90 @@ extension TSPTPInputVC {
             make.height.equalTo(0)
             make.width.equalTo(k_ScreenWidth)
         }
-        
+
         cusStackView.addSpacing(length: collectionViewBtootm)
     }
 }
 
-extension TSPTPInputVC: UITextViewDelegate{
+extension TSPTPInputVC: UITextViewDelegate {
     // 实现 UITextViewDelegate 协议方法,控制 return 键行为
     func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
         if text == "\n" {
             // 当输入为换行符(即按下 return 键)时,执行相应操作
-            //sendBolck?(textView.text)
+            // sendBolck?(textView.text)
             clickView()
             return false
         }
         return true
     }
-    
-    func textViewDidChange(_ textView: UITextView){
+
+    func textViewDidChange(_ textView: UITextView) {
         clearBtn.isHidden = textView.text.count <= 0
         viewModel.selectedPTPStyleModel.inputText = textView.text
         viewModel.gennerateChange()
     }
-   
 }
 
 extension TSPTPInputVC {
-    
     @objc func vipInfoChanged() {
         kExecuteOnMainThread {
             self.updateVipView()
         }
     }
+
     func updateVipView() {
         kExecuteOnMainThread {
             self.vipBtn.isHidden = PurchaseManager.default.isVip
             self.creatBtnView.updateVipView()
         }
     }
-    
-    func getVipText()->String{
+
+    func getVipText() -> String {
         return "Generate".localized
     }
- 
-
 }
+
 extension TSPTPInputVC {
-    
     @objc func clickView() {
         view.endEditing(true)
     }
-    
+
     func generateImage() {
-        
         viewModel.selectedPTPStyleModel.upLoadImage = viewModel.upLoadImage
         viewModel.selectedPTPStyleModel.upLoadImageUrl = nil
-        let gennerateVC = TSPTPGeneratorVC(generateStyleModel: viewModel.selectedPTPStyleModel) { [weak self] model in
+        let gennerateVC = TSPTPGeneratorVC(generateStyleModel: viewModel.selectedPTPStyleModel) { [weak self] _ in
             guard let self = self else { return }
             updateVipView()
         }
-        
-        gennerateVC.reloadViewBlock = { [weak self]  in
+
+        gennerateVC.reloadViewBlock = { [weak self] in
             guard let self = self else { return }
             updataCollectionView()
         }
-        
+
         gennerateVC.closePageComplete = {
-            [weak self]  in
+            [weak self] in
             guard let self = self else { return }
             updataCollectionView()
         }
-        
-        kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
+
+        kPresentModalVC(target: self, modelVC: gennerateVC, transitionStyle: .crossDissolve)
     }
-    
+
     func setCreatBtnEnabled() {
         let isAvailability = TSGeneratePTPOperationQueue.shared.isAvailability
-        if viewModel.isCanGennerate,isAvailability {
+        if viewModel.isCanGennerate, isAvailability {
             creatBtnView.setBtnEnabled(isEnabled: true)
             creatBtnView.loading = false
             dePrint("TSTextGeneralPicVC setCreatBtnEnabled false")
-        }else{
+        } else {
             dePrint("TSTextGeneralPicVC setCreatBtnEnabled = \(isAvailability)")
             creatBtnView.setBtnEnabled(isEnabled: false)
             creatBtnView.loading = !isAvailability
         }
-        
+
         updateVipView()
     }
-
 }
 
 extension TSPTPInputVC {
@@ -533,26 +518,36 @@ extension TSPTPInputVC {
         let textViewFrame = scrollView.convert(customTextView.frame, from: customTextView.superview)
         let scrollDistance = textViewFrame.maxY - (scrollView.bounds.height - keyboardHeight)
         let y = scrollDistance
-        scrollView.setContentOffset(CGPoint(x: 0, y: y),animated: true)
+        scrollView.setContentOffset(CGPoint(x: 0, y: y), animated: true)
     }
-    
+
     // MARK: - 键盘隐藏时恢复
+
     @objc private func keyboardWillHide(_ notification: Notification) {
         cusStackView.scrollView.contentOffset = CGPoint(x: 0, y: 0)
     }
 }
+
 extension TSPTPInputVC {
-    
-    func updateTextFiledView () {
+    func updateTextFiledView() {
         if viewModel.selectedPTPStyleModel.input {
             promptTextView.isHidden = false
-        }else{
+        } else {
             promptTextView.isHidden = true
         }
-        
+
         UIView.animate(withDuration: 0.3) {
             self.cusStackView.layoutIfNeeded()
         }
     }
 }
 
+extension TSPTPInputVC {
+    @objc func autoSelectImageToImageStyle(notify: Notification) {
+        if let objc = notify.userInfo as? [String: String] {
+            WindowHelper.resetToTabBarController(at: 0)
+            let styleId = objc.safeString(forKey: "styleId")
+            selectStyleView.selectStyle(styleId: styleId)
+        }
+    }
+}

+ 28 - 22
AIEmoji/Business/TSPTPGeneratorVC/TSPTPInputVC/View/TSPTPSelectStyleView.swift

@@ -5,33 +5,32 @@
 //  Created by 100Years on 2025/4/7.
 //
 
-class TSPTPSelectStyleView : TSBaseView{
+class TSPTPSelectStyleView: TSBaseView {
     var viewH: CGFloat = 108.0
-    var dataArray: [TSGenerateStyleModel] = [TSGenerateStyleModel](){
-        didSet{
+    var dataArray: [TSGenerateStyleModel] = [TSGenerateStyleModel]() {
+        didSet {
             styleCollectionView.reloadData()
-            
+
             if dataArray.count > 0 {
 //                DispatchQueue.main.async {
-                self.styleCollectionView.selectItem(at: self.currentIndexPath, animated: false, scrollPosition: .centeredHorizontally)
+                styleCollectionView.selectItem(at: currentIndexPath, animated: false, scrollPosition: .centeredHorizontally)
 //                }
             }
         }
     }
-    
-    var clickHandle:((IndexPath,TSGenerateStyleModel)->Void)?
+
+    var clickHandle: ((IndexPath, TSGenerateStyleModel) -> Void)?
     lazy var layout: UICollectionViewFlowLayout = {
         let layout = UICollectionViewFlowLayout()
         layout.scrollDirection = .horizontal
-        let w = (k_ScreenWidth-32.0-30.0-2.0)/4.0
+        let w = (k_ScreenWidth - 32.0 - 30.0 - 2.0) / 4.0
         layout.itemSize = CGSize(width: w, height: viewH)
         layout.minimumInteritemSpacing = 0.0
         layout.minimumLineSpacing = 10.0
         layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
         return layout
     }()
-    
-    
+
     lazy var styleCollectionView: UICollectionView = {
         let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
         collectionView.delegate = self
@@ -46,7 +45,7 @@ class TSPTPSelectStyleView : TSBaseView{
         return collectionView
     }()
 
-    var currentIndexPath:IndexPath = IndexPath(item: 0, section: 0)
+    var currentIndexPath: IndexPath = IndexPath(item: 0, section: 0)
 
     override func creatUI() {
         currentIndexPath = IndexPath(item: 0, section: 0)
@@ -55,36 +54,43 @@ class TSPTPSelectStyleView : TSBaseView{
             make.edges.equalToSuperview()
         }
     }
-    
-    func unCheck(){
+
+    func selectStyle(styleId: String) {
+        let index = dataArray.firstIndex {
+            $0.style == styleId
+        } ?? 0
+
+        let indexPath = IndexPath(item: index, section: 0)
+        collectionView(styleCollectionView, didSelectItemAt: indexPath)
+    }
+
+    func unCheck() {
         styleCollectionView.deselectItem(at: currentIndexPath, animated: true)
     }
 }
 
-extension TSPTPSelectStyleView: UICollectionViewDataSource ,UICollectionViewDelegate {
-    
+extension TSPTPSelectStyleView: UICollectionViewDataSource, UICollectionViewDelegate {
     public func numberOfSections(in collectionView: UICollectionView) -> Int {
         return 1
     }
-    
+
     public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
         return dataArray.count
     }
-    
+
     public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-        
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TSGennertatorSelectStyleCell.cellID, for: indexPath)
-        if let cell = cell as? TSGennertatorSelectStyleCell,let itemModel = dataArray.safeObj(At: indexPath.item){
+        if let cell = cell as? TSGennertatorSelectStyleCell, let itemModel = dataArray.safeObj(At: indexPath.item) {
             cell.itemModel = itemModel
         }
         return cell
     }
 
     public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-        if let model = dataArray.safeObj(At: indexPath.item){
+        if let model = dataArray.safeObj(At: indexPath.item) {
             currentIndexPath = indexPath
-            self.styleCollectionView.selectItem(at: self.currentIndexPath, animated: true, scrollPosition: .centeredHorizontally)
-            clickHandle?(indexPath,model)
+            styleCollectionView.selectItem(at: currentIndexPath, animated: true, scrollPosition: .centeredHorizontally)
+            clickHandle?(indexPath, model)
         }
     }
 }

+ 1 - 1
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/M/TSPTPStyleModel.swift

@@ -27,7 +27,7 @@ class TSGenerateStyleModel: TSBaseModel {
         isVip                   <- map["isVip"]
         specialStyle            <- map["specialStyle"]
         clickType               <- map["clickType"]
-        style                   <- map["style"]
+        style                   <- map["styleId"]
         input                   <- map["input"]
         advance                 <- map["advance"]
     }

+ 22 - 0
AIEmoji/Common/Ex/TSCustomAlertController+Ext.swift

@@ -0,0 +1,22 @@
+//
+//  TSCustomAlert+Ext.swift
+//  AIEmoji
+//
+//  Created by nkl on 2025/5/8.
+//
+
+import Foundation
+
+extension TSCustomAlertController {
+    /// 确认弹窗
+    public static func showAlert(on viewController: UIViewController, title: String?, message: String?, confirmTitle: String = "OK", confirmHandler: (() -> Void)? = nil) {
+        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
+
+        let confirmAction = UIAlertAction(title: confirmTitle, style: .default) { _ in
+            confirmHandler?()
+        }
+
+        alert.addAction(confirmAction)
+        viewController.present(alert, animated: true, completion: nil)
+    }
+}

+ 53 - 0
AIEmoji/Common/SchemeHandler/SchemeHandler+Ext.swift

@@ -0,0 +1,53 @@
+//
+//  SchemeHandler+Open.swift
+//  AIEmoji
+//
+//  Created by nkl on 2025/5/8.
+//
+
+import Foundation
+
+extension SchemeHandler {
+    // MARK: - 私有处理方法
+
+    func handleDefault() {
+        // 处理默认情况(无路径或未知路径)
+        guard let topVc = WindowHelper.topViewController() else {
+            debugPrint("🙅scheme 没有相应匹配,检查scheme 路由及参数")
+            return
+        }
+        TSCustomAlertController.showAlert(on: topVc, title: "The activity has expired, come next time".localized, message: nil)
+    }
+
+    func handleImageToImagePick(params: [URLQueryItem]?) {
+        var info: [String: Any]?
+        if let styleId = params?.first(where: { $0.name == "styleId" })?.value {
+            info = ["styleId": styleId]
+        }
+
+        NotificationCenter.default.post(
+            name: .schemeImageToImageStylePick,
+            object: nil,
+            userInfo: info)
+    }
+
+    /// 打开文生图页面
+    func handleTextToImageOpen(params: [URLQueryItem]?) {
+        WindowHelper.resetToTabBarController(at: 1)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
+            if let top = WindowHelper.topViewController() {
+                kPushVC(target: top, modelVC: TSTextGeneralPictureVC())
+            }
+        }
+    }
+
+    /// 打开母亲节活动页面
+    func handleMotherPolaroidOpen(params: [URLQueryItem]?) {
+        // TODO: 跳转 母亲节页面,参考文生图
+    }
+
+    /// 打开猫咪拟人化页面
+    func handlePetHumanizationOpen(params: [URLQueryItem]?) {
+        // TODO: 跳转猫咪拟人化页面,参考文生图
+    }
+}

+ 56 - 0
AIEmoji/Common/SchemeHandler/SchemeHandler.swift

@@ -0,0 +1,56 @@
+//
+//  SchemeHandler.swift
+//  AIEmoji
+//
+//  Created by nkl on 2025/5/8.
+//
+
+/**
+ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️
+ shceme 定义规则
+ 参考:
+ aiemoji://ImageToImage?style=Y2K
+ 路径 ImageToImage
+
+ 复杂逻辑发送对应通知,在对应位置处理相应逻辑,这里只做参数解析(注意:做好兜底逻辑)
+ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️
+ */
+
+import Foundation
+
+class SchemeHandler {
+    static let shared = SchemeHandler()
+
+    private init() {}
+    
+    func handle(url: URL) -> Bool {
+        guard url.scheme == "aiemoji" else { return false }
+
+        let path = url.host ?? ""
+        let params = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
+
+        print("Scheme received - Path: \(path), Params: \(params ?? [])")
+
+        switch path {
+        case "ImageToImage":
+            handleImageToImagePick(params: params)
+        case "TextToImage":
+            handleTextToImageOpen(params: params)
+        case "MotherPolaroid":
+            handleMotherPolaroidOpen(params: params)
+        case "PetHumanization":
+            handlePetHumanizationOpen(params: params)
+        default:
+            handleDefault()
+        }
+
+        return true
+    }
+}
+
+// MARK: - 通知扩展
+
+// 命名规则 Scheme+(功能)+Notification
+extension Notification.Name {
+    static let schemeImageToImageStylePick = Notification.Name("SchemeImageToImageStylePickNotification")
+}

+ 18 - 0
AIEmoji/Common/Tool/WindowHelper.swift

@@ -68,4 +68,22 @@ class WindowHelper {
         debugPrint("当前顶层 VC 是: \(String(describing: base))")
         return base
     }
+    
+    static func resetToTabBarController(at index: Int) {
+        guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }),
+              let tabBarController = window.rootViewController as? UITabBarController else {
+            return
+        }
+
+        // 关闭所有模态视图
+        window.rootViewController?.dismiss(animated: false) {
+            // 获取当前选中的 navigationController
+            if let nav = tabBarController.selectedViewController as? UINavigationController {
+                nav.popToRootViewController(animated: false)
+            }
+
+            // 切换到目标 tab
+            tabBarController.selectedIndex = index
+        }
+    }
 }

+ 13 - 0
AIEmoji/Info.plist

@@ -2,6 +2,19 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>CFBundleURLTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>CFBundleURLName</key>
+			<string>com.girl.music.wallpaper</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>aiemoji</string>
+			</array>
+		</dict>
+	</array>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 38 - 6
AIEmoji/Res/photo_to_photo_style.json

@@ -5,6 +5,7 @@
         "prompt":"Turn uploaded photos into ghibli style",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-02",
         "advance":true
     },
     {
@@ -12,6 +13,7 @@
         "imageText": "Anime Lite",
         "prompt":"Studio Ghibli-inspired anime aesthetic, Hayao Miyazaki style, vibrant yet soft color palette, detailed hand-painted textures, whimsical dreamlike atmosphere, soft cel-shading, watercolor wash effect, subtle grain texture, preserving original composition. Style strength: 85%",
         "specialStyle":0,
+        "styleId": "ImageToImage-01",
         "isVip": false
     },
     {
@@ -21,12 +23,14 @@
         "input":true,
         "specialStyle":1,
         "isVip": true,
+        "styleId": "ImageToImage-04",
         "advance":true
     },
     {
         "imageName": "ptp_style_7",
         "imageText": "Fantasy Illustration",
         "prompt":"将图片转化成精美插画手绘风格,保留人物主体和背景",
+        "styleId": "ImageToImage-05",
         "isVip": false
     },
     {
@@ -35,12 +39,14 @@
         "prompt":"用 irasutoya 风格绘制上传的照片,保留关键内容和构图,色彩丰富明亮",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-09",
         "advance":true
     },{
         "imageName": "ptp_style_chibi",
         "imageText": "Chibi",
         "prompt":"Turn the uploaded photo into a chibi sticker set of 4 pictures,every sticker should not cover the other stickers",
         "specialStyle":2,
+        "styleId": "ImageToImage-03",
         "isVip": true,
         "advance":true
     },
@@ -50,6 +56,7 @@
         "prompt":"Q版人物形象,3D黏土风,是真实世界的光影,保留原始人物细节,包括服装和造型。整体放在一张拍立得照片中,有一只手握着拍立得相纸。图中角色从拍立得相纸中,突破边框,延伸进入现实世界的二维空间。背景延续拍立得中的背景,Q版风格,背景不需要再有人物出现,仅作为拍立得中背景的延展,与照片原始场景一致。拍立得底部文字是“Life Moment”。",
         "specialStyle":2,
         "isVip": true,
+        "styleId": "ImageToImage-07",
         "advance":true
     },
     {
@@ -58,6 +65,7 @@
         "prompt":"A soft, high-quality plush toy of the uploaded photo's mainperson, with an oversized head, small body, and stubby limbs. It's made of fuzzy fabric, has embroidered facial features, and is shown sitting or standing against a neutral background. The expression is cute or expressive, and it wears simple clothes or iconic accessories if relevant. The lighting is soft and even, with a realistic, collectible toy look. Centered, full-body view.",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-06",
         "advance":true
     },
     {
@@ -66,6 +74,7 @@
         "prompt":"根据照片上的内容打造一款细致精美、萌趣可爱的3D渲染收藏摆件,装置在柔和粉彩色调、温馨浪漫的展示盒中。展示盒为浅奶油色搭配柔和的金色装饰,形似精致的便携珠宝盒。打开盒盖,呈现出一幕温暖浪漫的场景:两位Q版角色正甜蜜相望。盒顶雕刻着“FOREVER TOGETHER”(永远在一起)的字样,周围点缀着小巧精致的星星与爱心图案。两人都拥有充满表现力的眼睛,以及柔和、温暖的微笑,传递出浓浓的爱意和迷人的气质。他们身后有一扇圆形窗户,透过窗户能看到阳光明媚的中国古典小镇天际线和轻柔飘浮的云朵。盒内以温暖的柔和光线进行照明,背景中漂浮着花瓣点缀气氛。整个展示盒和角色的色调优雅和谐,营造出一个奢华而梦幻的迷你纪念品场景。",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-10",
         "advance":true
     },
     {
@@ -74,6 +83,7 @@
         "prompt":"将照片中的人物生成泡泡玛特风格手办,为泡泡玛特盲盒形式,3d 公仔在包装盒外面,采用正面视角,精致可爱O版的潮玩风格,公仔旁边放着正方形包装盒,和公仔有一点距离,包装盒为2d公仔的设计,包装盒正面图案为公仔正面形象。包装上带有文字 FunPop,图片背景根据上传图片的颜色自适应",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-11",
         "advance":true
     },
     {
@@ -82,6 +92,7 @@
         "prompt":"请根据上传的照片生成AirPods充电盒手办,照片的人物探出AirPods充电盒,一只手扶着盒子边缘,一只手拿着咖啡杯(或者打招呼),营造可爱且俏皮的感觉。充电盒细节 :Apple AirPods充电盒设计,摆放角度为45度角摆放,让内部结构更清楚。内部摆放顺序: 照片中的人物,AirPods 耳机(只保留一个耳机,摆放于盒内)。整体风格要可爱3D盲盒风格,人物相似度高",
         "specialStyle":2,
         "isVip": true,
+        "styleId": "ImageToImage-13",
         "advance":true
     },
     {
@@ -90,6 +101,7 @@
         "prompt":"把照片变成梵高的画风,保留人物和背景,要符合梵高风格的色彩手法,整体和谐好看",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-22",
         "advance":true
     },
     {
@@ -98,6 +110,7 @@
         "prompt":"请把上传的照片生成 switch 版动物森友会的风格,要和游戏中一样有立体质感,保留照片人物本身的表情和动作",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-14",
         "advance":true
     },
     {
@@ -106,6 +119,7 @@
         "prompt":"将这张照片生成为 chiikawa 画风,并让吉伊小八乌萨奇围绕在人物身边,耳朵要圆的,不要把人物变成 chiikawa",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-15",
         "advance":true
     },
     
@@ -124,6 +138,7 @@
         "prompt":"把上传的照片转化成Y2K千禧风格,保留原本人物主体",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-24",
         "advance":true
     },
     
@@ -133,6 +148,7 @@
         "prompt":"把上传的图片转换成中式剪纸风格,保留人物主体",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-35",
         "advance":true
     },
     {
@@ -141,6 +157,7 @@
         "prompt":"把上传的图片转换成治愈系简笔画风格,保留人物主体",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-18",
         "advance":true
     },
     {
@@ -149,6 +166,7 @@
         "prompt":"把上传的图片转换成手绘水彩风格,保留人物主体",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-29",
         "advance":true
     },
     
@@ -158,6 +176,7 @@
         "prompt":"Turn uploaded photos into Neon style",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-32",
         "advance":true
     },
     
@@ -167,6 +186,7 @@
         "prompt":"把上传的图片变成黑白日本漫画风格,不要改变人物主体",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-23",
         "advance":true
     },
     {
@@ -175,6 +195,7 @@
         "prompt":"把图片转换成儿童蜡笔风格,线条歪歪扭扭、色彩鲜艳丰富、充满童趣和表现力,像小朋友自由涂鸦出来的画",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-30",
         "advance":true
     },
     {
@@ -183,6 +204,7 @@
         "prompt":"把上传的照片转化成针织娃娃风格,四肢使用彩虹色毛线编织,其他部位保持原画色编织,质感柔软",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-31",
         "advance":true
     },
     
@@ -192,6 +214,7 @@
         "prompt":"把上传的照片转化为手工纸艺风,温暖略带超现实感,用纸板、瓶盖和贴纸等材料拼贴而成,温暖俏皮的色调",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-34",
         "advance":true
     },
     
@@ -201,6 +224,7 @@
         "prompt":"色彩鲜艳、表现力强的 riso 印刷(Risograph)风格,保留人物本身的表情动作",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-36",
         "advance":true
     },
     
@@ -210,6 +234,7 @@
         "prompt":"把上传的图片转化成黑白简笔画风格,使用粗线条勾勒轮廓,图像无阴影无填色,线条干净,边缘柔和,图片背景用白色不要透明",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-37",
         "advance":true
     },
     {
@@ -218,6 +243,7 @@
         "prompt":"Turn uploaded photo into sketch style",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-25",
         "advance":true
     },
     {
@@ -225,12 +251,14 @@
         "imageText": "Claymation",
         "prompt":"粘土动画风特征",
         "specialStyle":0,
+        "styleId": "ImageToImage-26",
         "isVip": true
     },
     {
         "imageName": "ptp_style_6",
         "imageText": "Classical Painting",
         "prompt":"Baroque oil painting conversion:  Rembrandt chiaroscuro lighting (3:1 contrast ratio)  Vermeer's ultramarine glaze technique  Impasto brushwork (0.8mm relief depth)  Aged varnish patina simulation  17th-century earth pigment palette (ochre/umber/lead-tin yellow)  Canvas weave retention (100% texture mapping)  Max 2% anatomical adjustment",
+        "styleId": "ImageToImage-08",
         "isVip": false
     },
     {
@@ -238,6 +266,7 @@
         "imageText": "Disney",
         "prompt":"把图片变成disney风格",
         "specialStyle":0,
+        "styleId": "ImageToImage-21",
         "isVip": true
     },
     {
@@ -246,6 +275,7 @@
         "prompt":"Transform this photo into a Snoopy cartoon style, including Snoopy and Woodstock, with the same color and frame size as the original photo, don't change my face to Snoopy",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-19",
         "advance":true
     },
     {
@@ -254,6 +284,7 @@
         "prompt":"Please turn this picture into Hello Kitty cartoon style and add Hello Kitty, don't change my face to hello kitty",
         "specialStyle":0,
         "isVip": true,
+        "styleId": "ImageToImage-20",
         "advance":true
     },
     {
@@ -267,6 +298,7 @@
         "imageText": "3D",
         "prompt":"把图片变成 Pixar 风格",
         "specialStyle":0,
+        "styleId": "ImageToImage-12",
         "isVip": true
     },
     {
@@ -274,12 +306,14 @@
         "imageText": "Pixel",
         "prompt":"超精细等距像素艺术,严格16x16像素网格,怀旧游戏机调色板,智能选择性抗锯齿+邻近插值算法,点阵抖动优化",
         "specialStyle":0,
+        "styleId": "ImageToImage-27",
         "isVip": true
     },
     {
         "imageName": "ptp_style_4",
         "imageText": "Pop Art",
         "prompt":"波普艺术肖像:1.3, [上传的图片], 鲜明本戴圆点, 半调网格纹, 粗黑轮廓线, 高饱和霓虹色块, 安迪沃霍尔风格, 罗伊·利希滕斯坦影响, 丝网印刷质感, 漫画美学, 平面色彩过渡, 电光青|荧光粉|酸性黄配色方案, 原始表情保留, 无阴影 --no 渐变, 写实, 立体感",
+        "styleId": "ImageToImage-38",
         "isVip": false
     },
     {
@@ -287,18 +321,14 @@
         "imageText": "Ukiyoe",
         "prompt":"整体转换为浮世绘风格,保留原始人物面部细节",
         "specialStyle":0,
+        "styleId": "ImageToImage-33",
         "isVip": true
     },
     {
         "imageName": "ptp_style_1",
         "imageText": "Cyberpunk",
         "prompt":"保留上传的图片人物表情和动作,不改变主体形状,添加霓虹灯光(蓝紫色)、机械义体细节、全息投影,背景改为雨夜未来都市,高饱和度强对比,发光边缘,轻微故障艺术,--保持构图与原图一致",
-        "isVip": false
-    },
-    {
-        "imageName": "ptp_style_8",
-        "imageText": "Photorealism",
-        "prompt":"上传的照片变成超写实照片级细节:1.5,皮肤毛孔/发丝/织物纤维高清刻画,自然光影立体感,4K电影质感,蔡司85mm f/1.4镜头焦外虚化,100%原图主体保留,面部零修改 --no 卡通化 艺术化 模糊 噪点",
+        "styleId": "ImageToImage-17",
         "isVip": false
     },
     {
@@ -306,6 +336,7 @@
         "imageText": "Toy",
         "prompt":"把图片变成LEGO风格",
         "specialStyle":0,
+        "styleId": "ImageToImage-39",
         "isVip": true
     },
     {
@@ -315,6 +346,7 @@
         "specialStyle":0,
         "input":true,
         "isVip": false,
+        "styleId": "ImageToImage-40",
         "advance":true
     }
 ]

+ 2 - 0
AIEmoji/de.lproj/Localizable.strings

@@ -292,3 +292,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "Es wurde ungewöhnlich hohe Generierungsaktivität festgestellt. Sie könnten ein Bot sein. Bitte versuchen Sie es morgen erneut.";
 "Got it" = "Verstanden";
 "Aspect Ratio" = "Seitenverhältnis";
+
+"The activity has expired, come next time" = "Die Aktivität ist abgelaufen, kommen Sie das nächste Mal";

+ 3 - 0
AIEmoji/en.lproj/Localizable.strings

@@ -289,3 +289,6 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow.";
 "Got it" = "Got it";
 "Aspect Ratio" = "Aspect Ratio";
+
+
+"The activity has expired, come next time" = "The activity has expired, come next time";

+ 2 - 0
AIEmoji/es.lproj/Localizable.strings

@@ -289,3 +289,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "Se ha detectado una frecuencia de generación inusualmente alta. Podría ser un bot. Por favor, inténtelo de nuevo mañana.";
 "Got it" = "Entendido";
 "Aspect Ratio" = "Relación de aspecto";
+
+"The activity has expired, come next time" = "La actividad ha caducado, ven la próxima vez.";

+ 2 - 0
AIEmoji/ja.lproj/Localizable.strings

@@ -288,3 +288,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "生成頻度が異常に高いと検知されました。ボットの可能性があります。明日再度お試しください。";
 "Got it" = "了解しました";
 "Aspect Ratio" = "アスペクト比";
+
+"The activity has expired, come next time" = "アクティビティの有効期限が切れました。次回に来てください";

+ 2 - 0
AIEmoji/ko.lproj/Localizable.strings

@@ -294,3 +294,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "생성 빈도가 비정상적으로 높게 감지되었습니다. 봇일 가능성이 있습니다. 내일 다시 시도해 주세요.";
 "Got it" = "확인했습니다";
 "Aspect Ratio" = "종횡비";
+
+"The activity has expired, come next time" = "활동이 만료되었습니다. 다음에 오세요.";

+ 2 - 0
AIEmoji/pt-BR.lproj/Localizable.strings

@@ -288,3 +288,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "Detectamos uma frequência de geração anormalmente alta. Você pode ser um bot. Por favor, tente novamente amanhã.";
 "Got it" = "Entendido";
 "Aspect Ratio" = "Proporção";
+
+"The activity has expired, come next time" = "A atividade expirou, venha na próxima vez";

+ 2 - 0
AIEmoji/pt-PT.lproj/Localizable.strings

@@ -288,3 +288,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "Detectamos uma frequência de geração anormalmente alta. Você pode ser um bot. Por favor, tente novamente amanhã.";
 "Got it" = "Entendido";
 "Aspect Ratio" = "Proporção";
+
+"The activity has expired, come next time" = "A atividade expirou, venha na próxima vez";

+ 2 - 0
AIEmoji/zh-Hans.lproj/Localizable.strings

@@ -289,3 +289,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "检测到您的生成频率过高,您可能是机器人,请明日再来";
 "Got it" = "知道了";
 "Aspect Ratio" = "宽高比";
+
+"The activity has expired, come next time" = "活动已结束,下次再来";

+ 2 - 0
AIEmoji/zh-Hant.lproj/Localizable.strings

@@ -280,3 +280,5 @@
 "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow." = "檢測到您的生成頻率過高,您可能是機器人,請明日再來";
 "Got it" = "知道了";
 "Aspect Ratio" = "寬高比";
+
+"The activity has expired, come next time" = "活動結束,下次再來";

+ 2 - 2
Podfile.lock

@@ -137,8 +137,8 @@ SPEC CHECKSUMS:
   SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
   SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
   SwipeCellKit: 3972254a826da74609926daf59b08d6c72e619ea
-  TSSmalCoacopods: 6aa97167f0c76b16fc7d1fd1eb198bb6aece4f68
+  TSSmalCoacopods: b168e4c8870d2c5593476237ac94ebe2e7de492a
 
 PODFILE CHECKSUM: 2b99c499d2d0b8510ec7508631defa3f2e7debab
 
-COCOAPODS: 1.16.2
+COCOAPODS: 1.15.2