// // AIConfigWindowController.swift // PDF Reader Pro Edition // // Created by Niehaoyu on 2024/1/17. // import Cocoa @objcMembers class AIConfigWindowController: NSWindowController, NSTextFieldDelegate, NSTextViewDelegate { @IBOutlet weak var contendBox: NSBox! @IBOutlet weak var titleLabel: NSTextField! @IBOutlet weak var titleTipBtn: KMButton! @IBOutlet weak var uploadBGView: NSView! @IBOutlet weak var uploadItemBox: KMBox! @IBOutlet weak var uploadDesLabel: NSTextField! @IBOutlet weak var uploadPDFBtn: KMButton! @IBOutlet weak var fileBGView: NSView! @IBOutlet weak var fileEmptyImage: NSImageView! @IBOutlet weak var fileActionView: NSView! @IBOutlet weak var fileActionLabel: NSTextField! @IBOutlet weak var fileActionBtn: KMButton! @IBOutlet weak var filePDFBGView: NSView! @IBOutlet weak var filePDFHoverBtn: KMButton! @IBOutlet weak var pdfCoverView: NSView! @IBOutlet weak var pdfCoverImageView: NSImageView! @IBOutlet weak var removePDFBtn: NSButton! @IBOutlet weak var inputTextCountLabel: NSTextField! @IBOutlet weak var fileSizeTipView: NSView! @IBOutlet weak var fileSizeTipLabel: NSTextField! @IBOutlet weak var pdfNameLbl: NSTextField! @IBOutlet var fileEmptyTextView: NSTextView! @IBOutlet weak var filePlaceholdLabel: NSTextField! @IBOutlet weak var fileContendViewTopConst: NSLayoutConstraint! @IBOutlet weak var translateSepLineView: NSView! @IBOutlet weak var translateConfigView: NSView! @IBOutlet weak var fromLanguageView: NSView! @IBOutlet weak var fromLanguageLabel: NSTextField! @IBOutlet weak var fromLanguageBtn: NSButton! @IBOutlet weak var toLanguageView: NSView! @IBOutlet weak var toLanguageLabel: NSTextField! @IBOutlet weak var toLanguageBtn: NSButton! @IBOutlet weak var translateResultView: NSView! @IBOutlet weak var translateResultLabel: NSTextField! @IBOutlet weak var translateResultSubLabel: NSTextField! @IBOutlet weak var resultBGView: NSView! @IBOutlet weak var resultEmptyImage: NSImageView! // @IBOutlet weak var resultLabel: NSTextField! @IBOutlet var aiResultTextView: NSTextView! @IBOutlet weak var cancelBtn: NSButton! @IBOutlet weak var saveAsTextBtn: NSButton! @IBOutlet weak var saveAsPDFBtn: NSButton! static var currentWindowController: AIConfigWindowController! var popOver: NSPopover! var configType: AIConfigType = .none var cancelHandle: ((_ windowVC: AIConfigWindowController) -> Void)? var fileActionenAble: Bool = false var aiConvertFinish: Bool = true var filePath: String = "" var fromLanguage: String = "" var toLanguage: String = "" var translateFinish: Bool = false var translateFile: Bool = false var translateResultPath: String = "" @objc static func currentWC() -> AIConfigWindowController { if currentWindowController != nil { return currentWindowController } else { let configWC: AIConfigWindowController = AIConfigWindowController.init(windowNibName: "AIConfigWindowController") currentWindowController = configWC; return currentWindowController } } override func windowDidLoad() { super.windowDidLoad() // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. self.titleLabel.font = NSFont.SFProTextSemiboldFont(13) self.titleTipBtn.mouseMoveCallback = {[unowned self] mouseEntered in if mouseEntered { var tipString = NSLocalizedString("Recommended file size: 10M or less.", comment: "") if self.configType == .reWriting { tipString = NSLocalizedString("No more than 2000 characters.", comment: "") } else if self.configType == .proofreading { tipString = NSLocalizedString("No more than 2000 characters.", comment: "") } else if self.configType == .translate { tipString = NSLocalizedString("1 credit for every 10,000 characters", comment: "") } let popViewController = KMToolbarItemPopViewController.init() if self.popOver == nil { self.popOver = NSPopover.init() } self.popOver.contentViewController = popViewController self.popOver.animates = false self.popOver.behavior = .semitransient self.popOver.contentSize = (popViewController.view.frame.size) popViewController.updateWithHelpTip(helpTip: tipString) self.popOver.show(relativeTo: self.titleTipBtn.frame, of: (self.window?.contentView)!, preferredEdge: .maxY) } else { self.popOver.close() } } self.uploadItemBox.borderWidth = 0 self.uploadItemBox.cornerRadius = 1 self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0() self.uploadDesLabel.font = NSFont.SFProTextRegularFont(13) self.uploadDesLabel.textColor = KMAppearance.KMColor_Layout_H0() self.uploadDesLabel.stringValue = NSLocalizedString("Choose", comment: "") self.uploadPDFBtn.mouseMoveCallback = {[unowned self] mouseEntered in if mouseEntered { self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S_1() } else { self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0() } } self.fileBGView.wantsLayer = true self.fileBGView.layer?.cornerRadius = 6 self.fileBGView.layer?.masksToBounds = true self.fileBGView.layer?.borderWidth = 1 self.fileBGView.layer?.borderColor = KMAppearance.KMColor_Layout_L_2().cgColor self.fileSizeTipView.wantsLayer = true self.fileSizeTipLabel.font = NSFont.SFProTextRegularFont(12) self.fileSizeTipLabel.textColor = KMAppearance.KMColor_Layout_H0() self.fileSizeTipLabel.stringValue = NSLocalizedString("Please upload a file smaller than 10M.", comment: "") self.fileSizeTipView.isHidden = true self.pdfNameLbl.font = NSFont.SFProTextRegularFont(12) self.pdfNameLbl.textColor = KMAppearance.KMColor_Layout_H0() self.resultBGView.wantsLayer = true self.resultBGView.layer?.cornerRadius = 6 self.resultBGView.layer?.masksToBounds = true self.fileActionView.wantsLayer = true self.fileActionView.layer?.cornerRadius = 1 self.fileActionView.layer?.masksToBounds = true self.fileActionLabel.font = NSFont.SFProTextRegularFont(13) self.fileActionBtn.mouseMoveCallback = {[unowned self] mouseEntered in if self.fileActionenAble == true { if mouseEntered { self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M_1().cgColor } else { self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor } } } self.translateSepLineView.wantsLayer = true self.pdfCoverView.wantsLayer = true self.pdfCoverView.layer?.cornerRadius = 2 self.pdfCoverView.layer?.borderWidth = 2 let countFormatter = TextFieldFormatter.init() countFormatter.setMaximumLength(2000) self.inputTextCountLabel.formatter = countFormatter self.inputTextCountLabel.delegate = self if KMAppearance.isDarkMode() { self.contendBox.fillColor = NSColor(red: 44/255, green: 44/255, blue: 44/255, alpha: 1) self.titleLabel.textColor = NSColor.white self.resultBGView.layer?.backgroundColor = NSColor(red: 49/255, green: 49/255, blue: 49/255, alpha: 1).cgColor self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30() self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 251/255, green: 166/255, blue: 0, alpha: 1).cgColor self.translateSepLineView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor self.translateResultView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor self.pdfCoverView.layer?.borderColor = NSColor.white.withAlphaComponent(0.2).cgColor self.pdfCoverView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.1).cgColor } else { self.contendBox.fillColor = NSColor.white self.titleLabel.textColor = NSColor.black self.resultBGView.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30() self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 253/255, green: 239/255, blue: 212/255, alpha: 1).cgColor self.translateSepLineView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor self.translateResultView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor self.pdfCoverView.layer?.borderColor = NSColor.black.withAlphaComponent(0.12).cgColor self.pdfCoverView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.04).cgColor } self.fileEmptyTextView.backgroundColor = NSColor.clear self.fileEmptyTextView.delegate = self self.fileEmptyTextView.isEditable = false self.fileEmptyTextView.frame = (self.fileEmptyTextView.enclosingScrollView?.contentView.bounds) ?? .zero self.fileEmptyTextView.autoresizingMask = [.width, .height] self.window?.makeFirstResponder(nil) var languages = ["Automatic", "English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"] let menu = NSMenu.init() for idx in 0...languages.count-1 { let string = languages[idx] let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "") menuItem.tag = 1000 + idx menu.addItem(menuItem) } self.fromLanguageView?.menu = menu self.fromLanguage = "Automatic" languages = ["English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"] var toMenu = NSMenu.init() for idx in 0...languages.count-1 { let string = languages[idx] let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "") menuItem.tag = 3000 + idx toMenu.addItem(menuItem) } self.toLanguageView?.menu = toMenu self.toLanguage = "English" self.fromLanguageLabel.font = NSFont.SFProTextRegularFont(13) self.toLanguageLabel.font = NSFont.SFProTextRegularFont(13) self.fromLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0() self.toLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0() self.fromLanguageLabel.stringValue = self.fromLanguage self.toLanguageLabel.stringValue = self.toLanguage self.removePDFBtn.isHidden = true self.filePDFHoverBtn.mouseMoveCallback = {[unowned self] mouseEntered in if mouseEntered { self.removePDFBtn.isHidden = false } else { self.removePDFBtn.isHidden = true } } self.cancelBtn.title = NSLocalizedString("Cancel", comment: "") self.saveAsTextBtn.title = NSLocalizedString("Save as TXT", comment: "") self.saveAsPDFBtn.title = NSLocalizedString("Save as PDF", comment: "") self.translateResultLabel.stringValue = NSLocalizedString("Translation Completion", comment: "") self.translateResultSubLabel.stringValue = NSLocalizedString("Click \"Save as PDF\" to save the result as a PDF file.", comment: "") self.translateResultView.isHidden = true self.translateResultView.wantsLayer = true if KMAppearance.isDarkMode() { self.translateResultLabel.textColor = NSColor.white self.translateResultSubLabel.textColor = NSColor.white } else { self.translateResultLabel.textColor = NSColor.black.withAlphaComponent(0.5) self.translateResultSubLabel.textColor = NSColor.black.withAlphaComponent(0.5) } self.translateResultLabel.font = NSFont.SFProTextSemiboldFont(13) self.translateResultSubLabel.font = NSFont.SFProTextRegularFont(13) self.aiResultTextView.font = NSFont.SFProTextRegularFont(14) self.aiResultTextView.textColor = KMAppearance.KMColor_Layout_H0() self.aiResultTextView.backgroundColor = NSColor.clear self.aiResultTextView.isEditable = false self.fileEmptyTextView.font = NSFont.SFProTextRegularFont(14) self.fileEmptyTextView.textColor = KMAppearance.KMColor_Layout_H0() } func getFileSize(atPath filePath : String) -> CGFloat { guard let dict = try? FileManager.default.attributesOfItem(atPath: filePath) as NSDictionary else { return 0 } return CGFloat(dict.fileSize()) } func refreshUI() -> Void { if self.configType == .summarize { self.titleLabel.stringValue = NSLocalizedString("AI Summarize", comment: "") self.fileEmptyImage.image = NSImage(named: "ai_summary_Empty") } else if self.configType == .reWriting { self.titleLabel.stringValue = NSLocalizedString("AI Rewrite", comment: "") self.fileEmptyImage.image = NSImage(named: "ai_rewriting_empty") } else if self.configType == .proofreading { self.titleLabel.stringValue = NSLocalizedString("AI Proofread", comment: "") self.fileEmptyImage.image = NSImage(named: "ai_proofreading_empty") } else if self.configType == .translate { self.titleLabel.stringValue = NSLocalizedString("AI Translate", comment: "") self.fileEmptyImage.image = NSImage(named: "ai_translate_empty") } self.translateResultView.isHidden = true self.translateConfigView.isHidden = true self.translateSepLineView.isHidden = true if self.configType == .reWriting || self.configType == .proofreading { self.uploadBGView.isHidden = true self.fileContendViewTopConst.constant = 52 self.filePDFBGView.isHidden = true } else { self.uploadBGView.isHidden = false self.fileContendViewTopConst.constant = 92 self.filePDFBGView.isHidden = false if self.configType == .translate { self.fileContendViewTopConst.constant = 137 self.translateConfigView.isHidden = false self.translateSepLineView.isHidden = false } } self.reloadData() } func clearData() -> Void { self.fileEmptyTextView.string = "" self.aiResultTextView.string = "" self.filePath = "" self.translateFinish = false self.translateFile = false self.cancelBtn.isEnabled = true } func reloadData() -> Void { self.clearData() self.fileEmptyTextView.isEditable = true self.inputTextCountLabel.isHidden = true if self.configType == .summarize { self.fileEmptyTextView.isEditable = false self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by uploading a document (pdf).", comment: "") } else if self.configType == .reWriting { self.inputTextCountLabel.isHidden = false self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "") } else if self.configType == .proofreading { self.inputTextCountLabel.isHidden = false self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "") } else if self.configType == .translate { self.inputTextCountLabel.isHidden = false self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text, or uploading a document (pdf).", comment: "") } self.updateActionViewUI() self.updateResultViewUI() } //MARK: 更新左侧UI func updateActionViewUI() -> Void{ self.fileSizeTipView.isHidden = true if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 { self.enableActionView(true) self.filePlaceholdLabel.isHidden = true } else { self.enableActionView(false) self.filePlaceholdLabel.isHidden = false } if self.filePath.count > 0 { self.fileEmptyTextView.isHidden = true self.filePDFBGView.isHidden = false let document = CPDFDocument.init(url: URL(fileURLWithPath: self.filePath)) if document?.isLocked == true || document == nil { self.filePath = "" self.updateActionViewUI() return } DispatchQueue.main.async { let page = document?.page(at: 0) let image = page?.thumbnail(of: page!.size) self.pdfCoverImageView.image = image } let filePathURL = URL(fileURLWithPath: self.filePath) let parentDirectory = filePathURL.lastPathComponent self.pdfNameLbl.stringValue = parentDirectory self.inputTextCountLabel.isHidden = true } else { self.fileEmptyTextView.isHidden = false self.filePDFBGView.isHidden = true self.pdfCoverImageView.image = nil self.inputTextCountLabel.isHidden = false if self.configType == .summarize { self.inputTextCountLabel.isHidden = true } } self.inputTextCountLabel.stringValue = String(format: "%ld", self.fileEmptyTextView.string.count) + "/2000" if self.fileEmptyTextView.string.count == 2000 { self.inputTextCountLabel.textColor = KMAppearance.KMColor_Status_Err() } else { if KMAppearance.isDarkMode() { self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30() } else { self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30() } } if self.aiResultTextView.string.count > 0 && (self.fileEmptyTextView.string.count > 0 || self.filePath.count > 0){ self.fileActionLabel.stringValue = NSLocalizedString("Redo (1 credit)", comment: "") if self.configType == .translate && self.filePath.isEmpty == false { self.fileActionLabel.stringValue = NSLocalizedString("Redo", comment: "") } } else { if self.configType == .summarize { self.fileActionLabel.stringValue = NSLocalizedString("Summarize", comment: "") + NSLocalizedString("(1 credit)", comment: "") } else if self.configType == .reWriting { self.fileActionLabel.stringValue = NSLocalizedString("Rewrite", comment: "") + NSLocalizedString("(1 credit)", comment: "") } else if self.configType == .proofreading { self.fileActionLabel.stringValue = NSLocalizedString("Proofread", comment: "") + NSLocalizedString("(1 credit)", comment: "") } else if self.configType == .translate { self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "") + NSLocalizedString("(1 credit)", comment: "") if self.filePath.isEmpty == false { self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "") } } } } func enableActionView(_ enable: Bool) -> Void { if enable { self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor self.fileActionLabel.textColor = NSColor.white self.fileActionBtn.isEnabled = true } else { self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().withAlphaComponent(0.4).cgColor self.fileActionLabel.textColor = NSColor.white.withAlphaComponent(0.4) self.fileActionBtn.isEnabled = false } self.fileActionenAble = enable if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 { self.filePlaceholdLabel.isHidden = true self.fileEmptyImage.isHidden = true } else { self.filePlaceholdLabel.isHidden = false self.fileEmptyImage.isHidden = false } } func updateResultViewUI() -> Void{ if self.aiResultTextView.string.count > 0 { self.resultEmptyImage.isHidden = true self.saveAsTextBtn.isEnabled = true self.saveAsPDFBtn.isEnabled = true } else { self.resultEmptyImage.isHidden = false self.saveAsTextBtn.isEnabled = false self.saveAsPDFBtn.isEnabled = false } if self.configType == .translate && self.translateFinish == true { self.saveAsTextBtn.isEnabled = true self.saveAsPDFBtn.isEnabled = true if self.translateFile == true { self.translateResultView.isHidden = false self.saveAsTextBtn.isEnabled = false } else { self.translateResultView.isHidden = true } if translateResultView.isHidden == false { self.resultEmptyImage.isHidden = true } } } //MARK: WaitingView func addWaingView(_ view: NSView) -> Void { self.removeWaitingView(view) let wView = WaitingView.init(frame: view.bounds) wView.autoresizingMask = [.width, .height] view.addSubview(wView) wView.startAnimation() } func removeWaitingView(_ view: NSView) -> Void { for view in view.subviews { if view.className == WaitingView.className() { view.removeFromSuperview() break } } } func aiTranslateFinish() -> Void { self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() } //MARK: AI-Action func aiSummarize() -> Void { if FileManager.default.fileExists(atPath: self.filePath) { self.aiResultTextView.string = "" self.addWaingView(self.resultBGView) self.enableActionView(false) self.removePDFBtn.isEnabled = false self.aiConvertFinish = false self.updateResultViewUI() // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath, state: .extractSummaryFile) { wrapper in DispatchQueue.main.async { self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() let success = wrapper.success if success { let resultStr = wrapper.content self.aiResultTextView.string = resultStr self.updateActionViewUI() self.updateResultViewUI() } else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() return } } } } } func aiReWriting() -> Void { self.aiResultTextView.string = "" self.addWaingView(self.resultBGView) self.enableActionView(false) self.removePDFBtn.isEnabled = false self.aiConvertFinish = false self.updateResultViewUI() // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string, state: .rewrite) { wrapper in DispatchQueue.main.async { self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() let success = wrapper.success if success { let resultStr = wrapper.content self.aiResultTextView.string = resultStr self.updateActionViewUI() self.updateResultViewUI() } else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() return } } } } func aiProofreading() -> Void { self.aiResultTextView.string = "" self.addWaingView(self.resultBGView) self.enableActionView(false) self.removePDFBtn.isEnabled = false self.aiConvertFinish = false self.updateResultViewUI() // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string, state: .correctTypos) { wrapper in DispatchQueue.main.async { self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() let success = wrapper.success if success { let resultStr = wrapper.content self.aiResultTextView.string = resultStr self.updateActionViewUI() self.updateResultViewUI() } else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() return } } } } func aiTranslate() -> Void { } @objc func menuItemClick(_ item: NSMenuItem) { if item.tag < 2000 { self.fromLanguage = item.title } else { self.toLanguage = item.title } self.fromLanguageLabel.stringValue = self.fromLanguage self.toLanguageLabel.stringValue = self.toLanguage } //MARK: - IBAction @IBAction func chooseFileAction(_ sender: KMButton) { let openPanel = NSOpenPanel() openPanel.canChooseDirectories = true openPanel.canChooseFiles = true openPanel.allowsMultipleSelection = false; openPanel.allowedFileTypes = ["pdf"] openPanel.beginSheetModal(for: self.window!) { result in if result == .OK { let fileURL = openPanel.urls.first let fileSize = self.getFileSize(atPath: fileURL!.path) if fileSize/(1024*1024) > 10 { self.fileSizeTipView.isHidden = false DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) { self.fileSizeTipView.isHidden = true } } else { self.filePath = fileURL!.path self.fileEmptyTextView.string = "" self.updateActionViewUI() } } } } @IBAction func chooseLanguageAction(_ sender: NSButton) { if sender == self.fromLanguageBtn { let menu = self.fromLanguageView?.menu menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender) } else if sender == self.toLanguageBtn { let menu = self.toLanguageView?.menu menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender) } } @IBAction func removePDFAction(_ sender: NSButton) { self.filePath = "" self.updateActionViewUI() self.updateResultViewUI() } @IBAction func startAIFunction(_ sender: KMButton) { let newStatus: Bool = KMCloudServer.isConnectionAvailable() if !newStatus { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Please make sure your internet connection is available.", comment: "") alert.runModal() return } if self.configType == .summarize { self.aiSummarize() } else if self.configType == .reWriting { self.aiReWriting() } else if self.configType == .proofreading { self.aiProofreading() } else if self.configType == .translate { self.aiResultTextView.string = "" self.addWaingView(self.resultBGView) self.enableActionView(false) self.translateFinish = false self.translateResultView.isHidden = true self.updateResultViewUI() if self.filePath.count > 0 { self.translateFile = true self.removePDFBtn.isEnabled = false self.aiConvertFinish = false self.cancelBtn.isEnabled = false KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath, state: .fileTranslate, from: self.fromLanguage, to: self.toLanguage) { wrapper in DispatchQueue.main.async { var success = wrapper.success let resultStr = wrapper.content if success == false && resultStr == "501" { success = true } if success { let translateTipWC = AITranslateTipWindowController.currentWC() let infoDict: NSDictionary = wrapper.result var credit: Int = 0 if infoDict["credit"] != nil { credit = (infoDict["credit"] ?? "0") as! Int } var charCount: Int = 0 if infoDict["charCount"] != nil { charCount = (infoDict["charCount"] ?? "0") as! Int } translateTipWC.creditValid = true if resultStr == "501" { translateTipWC.creditValid = false } translateTipWC.spendCredits = credit translateTipWC.totalChart = charCount translateTipWC.actionHandle = {[weak self] wc, result in if result == true { self?.window?.endSheet(wc.window!) KMAIRequestServerManager.defaultManager.aiTranslationFileTranslateHandle(fileKey: resultStr, from: self!.fromLanguage, to: self!.toLanguage) { wrapper in let success = wrapper.success if success { DispatchQueue.main.async { self?.aiTranslateFinish() self?.cancelBtn.isEnabled = true self!.translateFinish = true self!.translateResultView.isHidden = false let resultStr = wrapper.content self!.translateResultPath = resultStr self!.updateActionViewUI() self!.updateResultViewUI() } } else { DispatchQueue.main.async { self?.aiTranslateFinish() self?.cancelBtn.isEnabled = true let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() } return } } } else { self?.cancelBtn.isEnabled = true self?.aiTranslateFinish() self?.window?.endSheet(wc.window!) } } self.window?.beginSheet(translateTipWC.window!) translateTipWC.reloadData() } else { self.cancelBtn.isEnabled = true self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() return } } }; } else { self.translateFile = false self.removePDFBtn.isEnabled = false self.aiConvertFinish = false // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string, state: .textTranslate, from: self.fromLanguage, to: self.toLanguage) { wrapper in DispatchQueue.main.async { self.aiConvertFinish = true self.removePDFBtn.isEnabled = true self.removeWaitingView(self.resultBGView) self.updateActionViewUI() let success = wrapper.success if success { self.translateFinish = true self.translateResultView.isHidden = false let resultStr = wrapper.content self.aiResultTextView.string = resultStr self.updateActionViewUI() self.updateResultViewUI() } else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = wrapper.content alert.runModal() self.updateResultViewUI() return } } }; } } } @IBAction func languageChangeAction(_ sender: Any) { let curLan = self.fromLanguage self.fromLanguage = self.toLanguage self.toLanguage = curLan self.fromLanguageLabel.stringValue = self.fromLanguage self.toLanguageLabel.stringValue = self.toLanguage } @IBAction func cancelAction(_ sender: NSButton) { DispatchQueue.main.async { self.removeWaitingView(self.resultBGView) self.updateActionViewUI() } if self.aiConvertFinish == false { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("The task is already in process, if you cancel the process, AI Credit will not be returned, please confirm if you still need to cancel?", comment: "") alert.addButton(withTitle: NSLocalizedString("Back ", comment: "")) alert.addButton(withTitle: NSLocalizedString("Confirm Cancel", comment: "")) let response = alert.runModal() if response.rawValue == 1001 { self.confirmCancelAction() } return } if self.aiResultTextView.string.count > 0 { if KMFunctionGuideWindowController.availableShow(.aiInfoResultSave) == true { KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave) let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("The results are not saved. Do you want to save them in other formats?", comment: "") alert.addButton(withTitle: NSLocalizedString("Yes", comment: "")) alert.addButton(withTitle: NSLocalizedString("No", comment: "")) let response = alert.runModal() if response.rawValue == 1001 { self.cancelAction(self.cancelBtn) } return } } self.clearData() guard let callBack = self.cancelHandle else { return } callBack(self) } func confirmCancelAction() -> Void { self.clearData() guard let callBack = self.cancelHandle else { return } callBack(self) } @IBAction func saveAsTextAction(_ sender: Any) { let fileName = "Untitled" let outputSavePanel = NSSavePanel() outputSavePanel.allowedFileTypes = ["txt"] outputSavePanel.nameFieldStringValue = fileName let result = outputSavePanel.runModal() if result == .OK { let filePath = outputSavePanel.url?.path do { try self.aiResultTextView.string.write(to: outputSavePanel.url!, atomically: true, encoding: .utf8) NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)]) KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave) } catch { } } } @IBAction func saveAsPDFAction(_ sender: Any) { let fileName = "Untitled" let outputSavePanel = NSSavePanel() outputSavePanel.allowedFileTypes = ["pdf"] outputSavePanel.nameFieldStringValue = fileName let result = outputSavePanel.runModal() if result == .OK { let filePath = outputSavePanel.url?.path do { if self.translateFile == true { do { if self.translateResultPath.count > 0 { if FileManager.default.fileExists(atPath: filePath!) { do { try FileManager.default.removeItem(atPath: filePath!) // print("删除旧文件成功") } catch { // print("删除旧文件失败:\(error)") } } try FileManager.default.copyItem(atPath: self.translateResultPath, toPath: filePath!) NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)]) } } catch { } } else { let text = self.aiResultTextView.string self.createPDF(from: text, saveTo: filePath!) NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)]) } KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave) } catch { } } } func textDidChange(_ notification: Notification) { if let textView = notification.object as? NSTextView, textView == self.fileEmptyTextView { // 获取文本字段的当前字符数 let currentText = textView.string let currentCount = currentText.count // 如果超过最大字符数,将文本截断为最大字符数,并设置回文本字段 if currentCount > 2000 { let endIndex = currentText.index(currentText.startIndex, offsetBy: 2000) let truncatedText = String(currentText[..