// // AIChatView.swift // PDF Reader Pro Edition // // Created by Niehaoyu on 2024/4/17. // import Cocoa class AIChatView: NSView, NibLoadable { @IBOutlet weak var contendView: NSView! @IBOutlet weak var scrollView: NSScrollView! @IBOutlet weak var collectionView: NSCollectionView! var chooseConfigHandle: ((_ view: AIChatView, _ clickType: AIConfigType) -> Void)? var cancelAIHandle: ((_ itemView: AIChatView, _ chatInfoModel: AIChatInfoModel) -> Void)? var continueAITranslateHandle: ((_ itemView: AIChatView, _ chatInfoModel: AIChatInfoModel) -> Void)? var redoHandle: ((_ view: AIChatView, _ model: AIChatInfoModel) -> Void)? override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. } override func layout() { super.layout() self.collectionView.reloadData() } override func awakeFromNib() { super.awakeFromNib() self.scrollView.backgroundColor = NSColor.clear self.scrollView.drawsBackground = false self.scrollView.scrollerStyle = .overlay self.collectionView.delegate = self self.collectionView.dataSource = self self.collectionView.backgroundColors = [NSColor.clear] self.collectionView.wantsLayer = true self.collectionView.layer?.backgroundColor = NSColor.clear.cgColor self.collectionView.register(AIChatDefaultTIpItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatDefaultTIpItemID")) self.collectionView.register(AIChatStringResultItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatStringResultItemID")) self.collectionView.register(AIChatFileInfoItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatFileInfoItemID")) self.collectionView.register(AIChatStringUploadItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatStringUploadItemID")) self.collectionView.register(AIChatTranslateResultItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatTranslateResultItemID")) } func reloadData() { self.collectionView.reloadData() DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) { let indexPath = Set([IndexPath(item: AIChatInfoManager.defaultManager.modelsArrM.count-1, section: 0)]) self.collectionView.scrollToItems(at: indexPath, scrollPosition: .bottom) } } func sizeOfString(_ string: String, _ font: NSFont, _ width: CGFloat) -> (CGSize) { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byWordWrapping let attributes: [NSAttributedString.Key: Any] = [ .font: font, .paragraphStyle:paragraphStyle ] let size = (string as NSString).boundingRect(with: NSSize(width: width, height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size return size } } extension AIChatView: NSCollectionViewDelegate { //当item被选中 // public func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { // print("点击") // let view = collectionView.item(at: indexPaths.first!) as! KMAdvertisementCollectionViewItem // // let content = view.model // // guard let callBack = didSelect else { return } // // content?.index = indexPaths.first!.item // callBack(self, content!) // } // // //当item取消选中 // public func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set) { // _ = collectionView.item(at: indexPaths.first!) as! KMAdvertisementCollectionViewItem // } } extension AIChatView: NSCollectionViewDataSource { public func numberOfSections(in collectionView: NSCollectionView) -> Int { return 1 } public func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { return AIChatInfoManager.defaultManager.modelsArrM.count } //返回对应的item自定义个体 public func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { if indexPath.item > AIChatInfoManager.defaultManager.modelsArrM.count { return NSCollectionViewItem() } let model = AIChatInfoManager.defaultManager.modelsArrM[indexPath.item] if model.infoType == .defaultTip { let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatDefaultTIpItemID"), for: indexPath) as! AIChatDefaultTIpItem view.clickHandle = {[unowned self] view, configType in guard let callBack = self.chooseConfigHandle else { return } callBack(self, configType) } view.reloadData() return view } else if model.infoType == .chatFileUpload { let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatFileInfoItemID"), for: indexPath) as! AIChatFileInfoItem view.chatInfoModel = model view.reloadData() return view } else if model.infoType == .chatStringUpload{ let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatStringUploadItemID"), for: indexPath) as! AIChatStringUploadItem view.chatInfoModel = model view.reloadData() return view } else if model.infoType == .chatStringResult { let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatStringResultItemID"), for: indexPath) as! AIChatStringResultItem view.chatInfoModel = model view.redoHandle = {[unowned self] itemView, model in guard let callBack = self.redoHandle else { return } callBack(self, itemView.chatInfoModel) } view.copyHandle = {[unowned self] itemView, model in let pasteboard = NSPasteboard.general pasteboard.clearContents() pasteboard.setString(model.chatResult, forType: .string) let contextString = NSLocalizedString("Copy Successfully!", comment: "") _ = CustomAlertView.alertView(message: contextString, fromView: self, withStyle: .black) } view.reloadData() view.setUI() view.refreshViewColor() return view } else if model.infoType == .chatTranslateResult { let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "AIChatTranslateResultItemID"), for: indexPath) as! AIChatTranslateResultItem view.chatInfoModel = model view.reloadData() view.cancelAIHandle = {[unowned self] itemView in guard let callBack = self.cancelAIHandle else { return } callBack(self, view.chatInfoModel) } view.startAIHandle = {[unowned self] itemView in guard let callBack = self.continueAITranslateHandle else { return } callBack(self, view.chatInfoModel) } view.redoHandle = {[unowned self] itemView, model in guard let callBack = self.redoHandle else { return } callBack(self, itemView.chatInfoModel) } view.copyHandle = {[unowned self] itemView, model in let pasteboard = NSPasteboard.general pasteboard.clearContents() pasteboard.setString(model.chatResult, forType: .string) let contextString = NSLocalizedString("Copy Successfully!", comment: "") _ = CustomAlertView.alertView(message: contextString, fromView: self, withStyle: .black) } view.setUI() return view } return NSCollectionViewItem() } // public func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView { // var nibName: String? // var view = NSView() // if kind == NSCollectionView.elementKindSectionHeader { // nibName = "KMAdvertisementCollectionHeadView" // // view = collectionView.makeSupplementaryView(ofKind: kind, withIdentifier: NSUserInterfaceItemIdentifier(rawValue: nibName!), for: indexPath) // if let view = view as? KMAdvertisementCollectionHeadView { // let model = self.data[indexPath.section] // view.model = model // } // } else if kind == NSCollectionView.elementKindSectionFooter { // nibName = "KMAdvertisementCollectionHeadView" // // view = collectionView.makeSupplementaryView(ofKind: kind, withIdentifier: NSUserInterfaceItemIdentifier(rawValue: nibName!), for: indexPath) // } // return view // } } extension AIChatView: NSCollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { if indexPath.item > AIChatInfoManager.defaultManager.modelsArrM.count { return NSSize(width: 0.01, height: 0.01) } let model = AIChatInfoManager.defaultManager.modelsArrM[indexPath.item] if model.infoType == .defaultTip { return NSSize(width: NSWidth(self.collectionView.frame), height: 160) } else if model.infoType == .chatFileUpload { return NSSize(width: NSWidth(self.collectionView.frame), height: 64) } else if model.infoType == .chatStringUpload { let height = self.sizeOfString(model.uploadContent, NSFont.SFProTextRegularFont(14), 216).height return NSSize(width: NSWidth(self.collectionView.frame), height: height + 24) } else if model.infoType == .chatStringResult { if model.aiConfigType == .summarize || model.aiConfigType == .reWriting || model.aiConfigType == .proofreading { if model.chatInfoState == .stateSuccess { let height = self.sizeOfString(model.chatResult, NSFont.SFProTextRegularFont(14), 216).height return NSSize(width: NSWidth(self.collectionView.frame), height: 28+48+height) } else if model.chatInfoState == .stateFailed { let height = self.sizeOfString(model.chatResult, NSFont.SFProTextRegularFont(13), 198).height return NSSize(width: NSWidth(self.collectionView.frame), height: 28+16+height) } return NSSize(width: NSWidth(self.collectionView.frame), height: 155+28) } return NSSize(width: NSWidth(self.collectionView.frame), height: 164) } else if model.infoType == .chatTranslateResult { if model.aiConfigType == .translate { if model.chatInfoState == .stateLoading { return NSSize(width: NSWidth(self.collectionView.frame), height: 155+28) } else if model.chatInfoState == .stateInfoConfirm { if model.creditsValid == true { return NSSize(width: NSWidth(self.collectionView.frame), height: 200) } else { return NSSize(width: NSWidth(self.collectionView.frame), height: 228) } } else if model.chatInfoState == .stateCancel { return NSSize(width: NSWidth(self.collectionView.frame), height: 96) } else if model.chatInfoState == .stateFailed { let height = self.sizeOfString(model.chatResult, NSFont.SFProTextRegularFont(13), 198).height return NSSize(width: NSWidth(self.collectionView.frame), height: 40+28+height) } else if model.chatInfoState == .stateSuccess { if model.filePath.isEmpty == false { //文件 return NSSize(width: NSWidth(self.collectionView.frame), height: 160) } else { //文字 let height = self.sizeOfString(model.chatResult, NSFont.SFProTextRegularFont(13), 216).height return NSSize(width: NSWidth(self.collectionView.frame), height: 110+height) } } } return NSSize(width: NSWidth(self.collectionView.frame), height: 155+28) } return NSSize(width: 1, height: 1) } public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> NSSize { return NSSize(width: 0, height: 0.01) } public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, referenceSizeForFooterInSection section: Int) -> NSSize { return NSSize(width: 0, height: 0.01) } public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 16 } public func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets { return NSEdgeInsetsMake(13, 0, 30, 0) } }