|
@@ -0,0 +1,756 @@
|
|
|
+//
|
|
|
+// AIInfoInputView.swift
|
|
|
+// PDF Reader Pro Edition
|
|
|
+//
|
|
|
+// Created by Niehaoyu on 2024/4/17.
|
|
|
+//
|
|
|
+
|
|
|
+import Cocoa
|
|
|
+import PDFKit
|
|
|
+
|
|
|
+class customTextView: NSTextView {
|
|
|
+
|
|
|
+ var firstResponderHandle: ((_ view: customTextView, _ isFirstResponder: Bool) -> Void)?
|
|
|
+
|
|
|
+ override func becomeFirstResponder() -> Bool {
|
|
|
+ guard let callBack = self.firstResponderHandle else {
|
|
|
+ return super.becomeFirstResponder()
|
|
|
+ }
|
|
|
+ callBack(self, true)
|
|
|
+
|
|
|
+ return super.becomeFirstResponder()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func resignFirstResponder() -> Bool {
|
|
|
+ guard let callBack = self.firstResponderHandle else {
|
|
|
+ return super.resignFirstResponder()
|
|
|
+ }
|
|
|
+ callBack(self, false)
|
|
|
+
|
|
|
+ return super.resignFirstResponder()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+protocol AIInfoInputViewDelegate: AnyObject {
|
|
|
+
|
|
|
+ func ai_InputViewDidChooseCurFile(aiInputView: AIInfoInputView)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class AIInfoInputView: NSView, NibLoadable, NSTextFieldDelegate, NSTextViewDelegate {
|
|
|
+
|
|
|
+ @IBOutlet weak var contendBox: NSBox!
|
|
|
+
|
|
|
+ @IBOutlet weak var typeEmptyTipView: NSView!
|
|
|
+ @IBOutlet weak var typeEmptyTipLabel: NSTextField!
|
|
|
+
|
|
|
+ @IBOutlet weak var infoContendView: NSView!
|
|
|
+
|
|
|
+ @IBOutlet weak var typeHeaderView: NSView!
|
|
|
+ @IBOutlet weak var titleLabel: NSTextField!
|
|
|
+ @IBOutlet weak var titleTipBtn: KMButton!
|
|
|
+ @IBOutlet weak var stringClearBtn: NSButton!
|
|
|
+
|
|
|
+ @IBOutlet weak var chooseFileBtnView: NSView!
|
|
|
+ @IBOutlet weak var chooseCurFileBtn: NSButton!
|
|
|
+ @IBOutlet weak var chooseFileBtn: NSButton!
|
|
|
+ @IBOutlet weak var chooseFileViewTopConst: NSLayoutConstraint!
|
|
|
+
|
|
|
+ @IBOutlet weak var fileSizeTipView: NSView!
|
|
|
+ @IBOutlet weak var fileSizeTipLabel: NSTextField!
|
|
|
+
|
|
|
+ @IBOutlet weak var placeholdLabel: NSTextField!
|
|
|
+ @IBOutlet weak var placeholdLabelTopConst: NSLayoutConstraint!
|
|
|
+
|
|
|
+ @IBOutlet weak var fileInfoContendView: NSView!
|
|
|
+ @IBOutlet weak var fileInfoImg: NSImageView!
|
|
|
+ @IBOutlet weak var fileNameLabel: NSTextField!
|
|
|
+ @IBOutlet weak var fileSizeLabel: NSTextField!
|
|
|
+ @IBOutlet weak var filePageCountLabel: NSTextField!
|
|
|
+ @IBOutlet weak var removeChooseFileBtn: NSButton!
|
|
|
+ @IBOutlet weak var fileInfoViewTopConst: NSLayoutConstraint!
|
|
|
+
|
|
|
+ @IBOutlet weak var textScrollView: NSScrollView!
|
|
|
+ @IBOutlet var fileEmptyTextView: customTextView!
|
|
|
+ @IBOutlet weak var textScrollViewTopConst: NSLayoutConstraint!
|
|
|
+
|
|
|
+
|
|
|
+ @IBOutlet weak var inputTextCountLabel: NSTextField!
|
|
|
+
|
|
|
+ @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 translateChangeBtn: KMButton!
|
|
|
+
|
|
|
+ @IBOutlet weak var startButton: NSButton!
|
|
|
+
|
|
|
+ weak var aiDelegate: AIInfoInputViewDelegate?
|
|
|
+
|
|
|
+ var area: NSTrackingArea?
|
|
|
+ var popOver: NSPopover!
|
|
|
+
|
|
|
+ var aiConfigType: AIConfigType = .none
|
|
|
+ var filePath: String = ""
|
|
|
+ var fromLanguage: String = ""
|
|
|
+ var toLanguage: String = ""
|
|
|
+
|
|
|
+ var startAIHandle: ((_ view: AIInfoInputView) -> Void)?
|
|
|
+
|
|
|
+ var inputFrameUpdateHandle: ((_ view: AIInfoInputView, _ stringSize: CGSize) -> Void)?
|
|
|
+
|
|
|
+ override func draw(_ dirtyRect: NSRect) {
|
|
|
+ super.draw(dirtyRect)
|
|
|
+
|
|
|
+ // Drawing code here.
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ override func awakeFromNib() {
|
|
|
+ super.awakeFromNib()
|
|
|
+
|
|
|
+ self.contendBox.borderWidth = 1
|
|
|
+ self.contendBox.cornerRadius = 7
|
|
|
+
|
|
|
+ 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.aiConfigType == .reWriting {
|
|
|
+ tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
|
|
|
+ } else if self.aiConfigType == .proofreading {
|
|
|
+ tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
|
|
|
+ } else if self.aiConfigType == .translate {
|
|
|
+ tipString = NSLocalizedString("1 credit for every 10,000 characters; No more than 10M of a document. ", 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)
|
|
|
+
|
|
|
+ let origin = self.titleTipBtn.superview?.convert(self.titleTipBtn.frame.origin, to: self)
|
|
|
+ var frame = self.titleTipBtn.frame
|
|
|
+ frame.origin.x = origin!.x + 12
|
|
|
+ frame.origin.y = origin!.y + 20
|
|
|
+ self.popOver.show(relativeTo: frame, of: (self.window?.contentView)!, preferredEdge: .maxY)
|
|
|
+
|
|
|
+ } else {
|
|
|
+ self.popOver.close()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let countFormatter = TextFieldFormatter.init()
|
|
|
+ countFormatter.setMaximumLength(2000)
|
|
|
+ self.inputTextCountLabel.formatter = countFormatter
|
|
|
+ self.inputTextCountLabel.delegate = self
|
|
|
+
|
|
|
+ self.fileEmptyTextView.font = NSFont.SFProTextRegularFont(14)
|
|
|
+ self.fileEmptyTextView.delegate = self
|
|
|
+ self.fileEmptyTextView.firstResponderHandle = {[unowned self] view, isFirstResponder in
|
|
|
+ if isFirstResponder {
|
|
|
+ if self.fileSizeTipView.isHidden == false {
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+ NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideFileSizeTipView), object: nil)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
|
|
|
+ self.setUpTranslateUI()
|
|
|
+ }
|
|
|
+
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+ 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.layer?.cornerRadius = 2
|
|
|
+ self.fileSizeTipView.layer?.masksToBounds = true
|
|
|
+
|
|
|
+ self.fileInfoContendView.wantsLayer = true
|
|
|
+ self.fileInfoContendView.layer?.cornerRadius = 6
|
|
|
+ self.fileInfoContendView.layer?.masksToBounds = true
|
|
|
+
|
|
|
+ self.translateConfigView.wantsLayer = true
|
|
|
+ self.translateConfigView.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
+
|
|
|
+ self.typeEmptyTipLabel.stringValue = NSLocalizedString("Select the AI tool", comment: "")
|
|
|
+
|
|
|
+ self.chooseCurFileBtn.title = NSLocalizedString("Current File", comment: "")
|
|
|
+ self.chooseFileBtn.title = NSLocalizedString("Choose", comment: "")
|
|
|
+
|
|
|
+ self.translateChangeBtn.mouseMoveCallback = {[unowned self] mouseEntered in
|
|
|
+ if mouseEntered {
|
|
|
+ self.translateChangeBtn.image = NSImage(named: "AIchange_hover")
|
|
|
+ } else {
|
|
|
+ self.translateChangeBtn.image = NSImage(named: "AIchange")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.updateCountLabelInfo()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ override func updateTrackingAreas() {
|
|
|
+ super.updateTrackingAreas()
|
|
|
+
|
|
|
+ if let _area = self.area, _area.rect.isEmpty == false {
|
|
|
+ if (_area.rect.equalTo(self.bounds)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (self.area != nil) {
|
|
|
+ self.removeTrackingArea(self.area!)
|
|
|
+ self.area = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ self.area = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .mouseMoved, .activeAlways], owner: self)
|
|
|
+ self.addTrackingArea(self.area!)
|
|
|
+ }
|
|
|
+
|
|
|
+ func setUpTranslateUI() {
|
|
|
+
|
|
|
+ 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
|
|
|
+ menuItem.target = self
|
|
|
+ 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"]
|
|
|
+ let 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
|
|
|
+ menuItem.target = self
|
|
|
+ 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
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func aiFunctionTypeChanged() {
|
|
|
+ self.refreshStringSize()
|
|
|
+ }
|
|
|
+
|
|
|
+ func reloadData() {
|
|
|
+ if AIChatInfoManager.defaultManager.currentFilePath.isEmpty || AIChatInfoManager.defaultManager.isAILoading {
|
|
|
+ self.chooseCurFileBtn.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.chooseCurFileBtn.isEnabled = true
|
|
|
+ }
|
|
|
+
|
|
|
+ if AIChatInfoManager.defaultManager.isAILoading {
|
|
|
+ self.chooseFileBtn.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.chooseFileBtn.isEnabled = true
|
|
|
+ }
|
|
|
+
|
|
|
+ self.startButton.title = NSLocalizedString("Start (1 credit)", comment: "")
|
|
|
+ if self.aiConfigType == .none {
|
|
|
+ self.typeEmptyTipView.isHidden = false
|
|
|
+ self.infoContendView.isHidden = true
|
|
|
+ } else {
|
|
|
+ self.typeEmptyTipView.isHidden = true
|
|
|
+ self.infoContendView.isHidden = false
|
|
|
+
|
|
|
+ self.chooseFileBtnView.isHidden = true
|
|
|
+ self.inputTextCountLabel.isHidden = false
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.textScrollView.isHidden = true
|
|
|
+ self.translateConfigView.isHidden = true
|
|
|
+ self.fileInfoContendView.isHidden = true
|
|
|
+ self.removeChooseFileBtn.isHidden = true
|
|
|
+
|
|
|
+ self.startButton.isEnabled = false
|
|
|
+ self.placeholdLabelTopConst.constant = -2
|
|
|
+ self.chooseFileViewTopConst.constant = 0
|
|
|
+ self.textScrollViewTopConst.constant = 0
|
|
|
+ self.fileInfoViewTopConst.constant = 8
|
|
|
+
|
|
|
+ self.stringClearBtn.isHidden = true
|
|
|
+ if self.fileEmptyTextView.string.isEmpty == false {
|
|
|
+ self.stringClearBtn.isHidden = false
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.aiConfigType == .summarize {
|
|
|
+ self.inputTextCountLabel.isHidden = true
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.chooseFileBtnView.isHidden = false
|
|
|
+ self.placeholdLabel.stringValue = NSLocalizedString("Summarize the current file or click choose other files.", comment: "")
|
|
|
+ self.placeholdLabelTopConst.constant = CGRectGetHeight(self.chooseFileBtnView.frame) + 4
|
|
|
+
|
|
|
+ if self.filePath.isEmpty == false {
|
|
|
+ self.fileInfoContendView.isHidden = false
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+
|
|
|
+ let filePathURL = URL(fileURLWithPath: self.filePath)
|
|
|
+ self.fileNameLabel.stringValue = filePathURL.lastPathComponent
|
|
|
+ self.fileSizeLabel.stringValue = self.fileSizeString(Float(self.getFileSize(atPath: self.filePath)))
|
|
|
+ if let document = PDFDocument(url: URL(fileURLWithPath: filePath)) {
|
|
|
+ self.filePageCountLabel.stringValue = String(format: "%d ", document.pageCount) + NSLocalizedString("Page", comment: "")
|
|
|
+ if document.pageCount > 1 {
|
|
|
+ self.filePageCountLabel.stringValue = String(format: "%d ", document.pageCount) + NSLocalizedString("Pages", comment: "")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ self.filePageCountLabel.stringValue = ""
|
|
|
+ }
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if self.aiConfigType == .reWriting {
|
|
|
+ self.textScrollView.isHidden = false
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ if self.fileEmptyTextView.string.isEmpty == false {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+ self.placeholdLabel.stringValue = NSLocalizedString("Enter or paste content here...", comment: "")
|
|
|
+
|
|
|
+ } else if self.aiConfigType == .proofreading {
|
|
|
+ self.textScrollView.isHidden = false
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ if self.fileEmptyTextView.string.isEmpty == false {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+ self.placeholdLabel.stringValue = NSLocalizedString("Enter or paste content here...", comment: "")
|
|
|
+
|
|
|
+ } else if self.aiConfigType == .translate {
|
|
|
+ self.textScrollView.isHidden = false
|
|
|
+ self.translateConfigView.isHidden = false
|
|
|
+ self.chooseFileBtnView.isHidden = false
|
|
|
+ self.chooseFileViewTopConst.constant = 30
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.placeholdLabelTopConst.constant = 58
|
|
|
+ self.textScrollViewTopConst.constant = 58
|
|
|
+
|
|
|
+ self.placeholdLabel.stringValue = NSLocalizedString("Enter or paste content here...", comment: "")
|
|
|
+
|
|
|
+ if self.filePath.isEmpty == false {
|
|
|
+ self.startButton.title = NSLocalizedString("Start", comment: "")
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.fileInfoContendView.isHidden = false
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.inputTextCountLabel.isHidden = true
|
|
|
+
|
|
|
+ self.fileInfoViewTopConst.constant = 8
|
|
|
+
|
|
|
+ let filePathURL = URL(fileURLWithPath: self.filePath)
|
|
|
+ self.fileNameLabel.stringValue = filePathURL.lastPathComponent
|
|
|
+ self.fileSizeLabel.stringValue = self.fileSizeString(Float(self.getFileSize(atPath: self.filePath)))
|
|
|
+
|
|
|
+ if let document = PDFDocument(url: URL(fileURLWithPath: filePath)) {
|
|
|
+ self.filePageCountLabel.stringValue = String(format: "%d ", document.pageCount) + NSLocalizedString("Page", comment: "")
|
|
|
+ if document.pageCount > 1 {
|
|
|
+ self.filePageCountLabel.stringValue = String(format: "%d ", document.pageCount) + NSLocalizedString("Pages", comment: "")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ self.filePageCountLabel.stringValue = ""
|
|
|
+ }
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+
|
|
|
+ } else {
|
|
|
+ self.startButton.title = NSLocalizedString("Start (1 credit)", comment: "")
|
|
|
+ self.textScrollView.isHidden = false
|
|
|
+ if self.fileEmptyTextView.string.isEmpty {
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.startButton.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ self.refreshUI()
|
|
|
+ }
|
|
|
+
|
|
|
+ func refreshUI() {
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.titleLabel.textColor = NSColor.white
|
|
|
+ self.contendBox.fillColor = NSColor.white.withAlphaComponent(0.05)
|
|
|
+ self.contendBox.borderColor = KMAppearance.KMColor_Interactive_M0().withAlphaComponent(1)
|
|
|
+ self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 251/255, green: 166/255, blue: 0, alpha: 1).cgColor
|
|
|
+
|
|
|
+ self.typeEmptyTipLabel.textColor = KMAppearance.KMColor_Interactive_S1()
|
|
|
+ self.placeholdLabel.textColor = KMAppearance.KMColor_Interactive_S1()
|
|
|
+ self.toLanguageView.layer?.backgroundColor = NSColor(red: 110/255, green: 109/255, blue: 112/255, alpha: 1).cgColor
|
|
|
+ self.fromLanguageView.layer?.backgroundColor = NSColor(red: 110/255, green: 109/255, blue: 112/255, alpha: 1).cgColor
|
|
|
+
|
|
|
+ } else {
|
|
|
+ self.titleLabel.textColor = NSColor.black
|
|
|
+ self.contendBox.borderColor = NSColor(red: 201/255, green: 218/255, blue: 247/255, alpha: 1)
|
|
|
+ self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 253/255, green: 239/255, blue: 212/255, alpha: 1).cgColor
|
|
|
+
|
|
|
+ self.typeEmptyTipLabel.textColor = KMAppearance.KMColor_Layout_B30()
|
|
|
+ self.placeholdLabel.textColor = KMAppearance.KMColor_Layout_B30()
|
|
|
+
|
|
|
+ self.toLanguageView.layer?.backgroundColor = NSColor(red: 236/255, green: 242/255, blue: 254/255, alpha: 1).cgColor
|
|
|
+ self.fromLanguageView.layer?.backgroundColor = NSColor(red: 236/255, green: 242/255, blue: 254/255, alpha: 1).cgColor
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if self.aiConfigType == .summarize {
|
|
|
+ self.titleLabel.stringValue = "#" + NSLocalizedString("AI Summarize", comment: "")
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 85/255, green: 245/255, blue: 1, alpha: 1)
|
|
|
+ } else {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 0, green: 209/255, blue: 222/255, alpha: 1)
|
|
|
+ }
|
|
|
+ } else if self.aiConfigType == .reWriting {
|
|
|
+ self.titleLabel.stringValue = "#" + NSLocalizedString("AI Rewrite", comment: "")
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 255/255, green: 105/255, blue: 195/255, alpha: 1)
|
|
|
+ } else {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 240/255, green: 28/255, blue: 155/255, alpha: 1)
|
|
|
+ }
|
|
|
+ } else if self.aiConfigType == .proofreading {
|
|
|
+ self.titleLabel.stringValue = "#" + NSLocalizedString("AI Proofread", comment: "")
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 194/255, green: 157/255, blue: 1, alpha: 1)
|
|
|
+ } else {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 108/255, green: 28/255, blue: 240/255, alpha: 1)
|
|
|
+ }
|
|
|
+ } else if self.aiConfigType == .translate {
|
|
|
+ self.titleLabel.stringValue = "#" + NSLocalizedString("AI Translate", comment: "")
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 255/255, green: 152/255, blue: 77/255, alpha: 1)
|
|
|
+ } else {
|
|
|
+ self.titleLabel.textColor = NSColor(red: 240/255, green: 101/255, blue: 0, alpha: 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.placeholdLabel.font = NSFont.SFProTextRegularFont(14)
|
|
|
+
|
|
|
+ self.fromLanguageView.wantsLayer = true
|
|
|
+ self.toLanguageView.wantsLayer = true
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func updateCountLabelInfo() {
|
|
|
+
|
|
|
+ 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()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 fileSizeString(_ fSize: Float) -> String {
|
|
|
+ let fileSize = fSize / 1024
|
|
|
+ let size = fileSize >= 1024 ? (fileSize < 1048576 ? fileSize/1024 : fileSize/1048576.0) : fileSize
|
|
|
+ let unit = fileSize >= 1024 ? (fileSize < 1048576 ? "M" : "G") : "K"
|
|
|
+ return String(format: "%0.1f %@", size, unit)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func hideFileSizeTipView() {
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: IBAction
|
|
|
+
|
|
|
+ @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
|
|
|
+ }
|
|
|
+
|
|
|
+ @IBAction func chooseCurFileAction(_ sender: Any) {
|
|
|
+
|
|
|
+ self.aiDelegate?.ai_InputViewDidChooseCurFile(aiInputView: self)
|
|
|
+
|
|
|
+
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+
|
|
|
+ let curFilePath = AIChatInfoManager.defaultManager.currentFilePath
|
|
|
+ let fileSize = self.getFileSize(atPath: curFilePath)
|
|
|
+ if fileSize/(1024*1024) > 10 {
|
|
|
+ self.fileSizeTipView.isHidden = false
|
|
|
+
|
|
|
+ NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideFileSizeTipView), object: nil)
|
|
|
+
|
|
|
+ self.perform(#selector(hideFileSizeTipView), with: nil, afterDelay: 5)
|
|
|
+
|
|
|
+ self.filePath = ""
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.reloadData()
|
|
|
+ } else {
|
|
|
+ self.filePath = curFilePath
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.reloadData()
|
|
|
+ }
|
|
|
+ self.mouseEntered(with: NSEvent())
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @IBAction func summaryUploadAction(_ sender: NSButton) {
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+
|
|
|
+ let openPanel = NSOpenPanel()
|
|
|
+ openPanel.canChooseDirectories = false
|
|
|
+ openPanel.canChooseFiles = true
|
|
|
+ openPanel.allowsMultipleSelection = false;
|
|
|
+ openPanel.allowedFileTypes = ["pdf","PDF"]
|
|
|
+ openPanel.beginSheetModal(for: self.window!) { [self] 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
|
|
|
+
|
|
|
+ NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideFileSizeTipView), object: nil)
|
|
|
+
|
|
|
+ self.perform(#selector(hideFileSizeTipView), with: nil, afterDelay: 5)
|
|
|
+
|
|
|
+ self.filePath = ""
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.reloadData()
|
|
|
+ } else {
|
|
|
+ self.filePath = fileURL!.path
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.reloadData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @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 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 removeChooseFile(_ sender: NSButton) {
|
|
|
+ if self.aiConfigType == .summarize ||
|
|
|
+ self.aiConfigType == .translate {
|
|
|
+
|
|
|
+ self.filePath = ""
|
|
|
+ self.reloadData()
|
|
|
+ self.updateCountLabelInfo()
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @IBAction func clearInputStringAction(_ sender: Any) {
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ self.reloadData()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @IBAction func startAIAction(_ sender: NSButton) {
|
|
|
+ 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
|
|
|
+ }
|
|
|
+
|
|
|
+ guard let callBack = self.startAIHandle else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callBack(self)
|
|
|
+
|
|
|
+ if self.aiConfigType == .summarize {
|
|
|
+ self.filePath = ""
|
|
|
+ } else if self.aiConfigType == .reWriting {
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ } else if self.aiConfigType == .proofreading {
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ } else if self.aiConfigType == .translate {
|
|
|
+ self.filePath = ""
|
|
|
+ self.fileEmptyTextView.string = ""
|
|
|
+ }
|
|
|
+
|
|
|
+ self.reloadData()
|
|
|
+
|
|
|
+ self.updateCountLabelInfo()
|
|
|
+
|
|
|
+ self.refreshStringSize()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func sizeOfString(_ string: String, _ font: NSFont) -> (CGSize) {
|
|
|
+ let attributes: [NSAttributedString.Key: Any] = [
|
|
|
+ .font: font
|
|
|
+ ]
|
|
|
+ let size = (string as NSString).boundingRect(with: NSSize(width: CGRectGetWidth(self.textScrollView.frame), height: CGFloat(MAXFLOAT)),
|
|
|
+ options: .usesLineFragmentOrigin,
|
|
|
+ attributes: attributes,
|
|
|
+ context: nil).size
|
|
|
+ return size
|
|
|
+ }
|
|
|
+
|
|
|
+ func refreshStringSize() {
|
|
|
+ var size = self.sizeOfString(self.fileEmptyTextView.string, self.fileEmptyTextView.font!)
|
|
|
+ if self.aiConfigType == .summarize {
|
|
|
+ if self.filePath.isEmpty == true {
|
|
|
+ size = self.sizeOfString(self.placeholdLabel.stringValue, self.placeholdLabel.font!)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ guard let callBack = self.inputFrameUpdateHandle else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callBack(self, size)
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: Deletegate
|
|
|
+ func textShouldBeginEditing(_ textObject: NSText) -> Bool {
|
|
|
+ print("textShouldBeginEditing")
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ 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[..<endIndex])
|
|
|
+ textView.string = truncatedText
|
|
|
+ }
|
|
|
+
|
|
|
+ self.refreshStringSize()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.aiConfigType == .reWriting {
|
|
|
+ if self.fileEmptyTextView.string.isEmpty {
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.startButton.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+ } else if self.aiConfigType == .proofreading {
|
|
|
+ if self.fileEmptyTextView.string.isEmpty {
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.startButton.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+ } else if self.aiConfigType == .translate {
|
|
|
+ if self.fileEmptyTextView.string.isEmpty {
|
|
|
+ self.placeholdLabel.isHidden = false
|
|
|
+ self.startButton.isEnabled = false
|
|
|
+ } else {
|
|
|
+ self.placeholdLabel.isHidden = true
|
|
|
+ self.startButton.isEnabled = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.updateCountLabelInfo()
|
|
|
+
|
|
|
+ self.stringClearBtn.isHidden = true
|
|
|
+ if self.fileEmptyTextView.string.isEmpty == false {
|
|
|
+ self.stringClearBtn.isHidden = false
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: MouseEvent
|
|
|
+ override func mouseEntered(with event: NSEvent) {
|
|
|
+ super.mouseEntered(with: event)
|
|
|
+
|
|
|
+ if self.filePath.isEmpty == false {
|
|
|
+ self.removeChooseFileBtn.isHidden = false
|
|
|
+ if KMAppearance.isDarkMode() {
|
|
|
+ self.fileInfoContendView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
|
|
|
+ } else {
|
|
|
+ self.fileInfoContendView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ self.fileInfoContendView.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mouseExited(with event: NSEvent) {
|
|
|
+ super.mouseExited(with: event)
|
|
|
+
|
|
|
+ self.removeChooseFileBtn.isHidden = true
|
|
|
+ self.fileInfoContendView.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mouseMoved(with event: NSEvent) {
|
|
|
+ super.mouseMoved(with: event)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mouseDown(with event: NSEvent) {
|
|
|
+ super.mouseDown(with: event)
|
|
|
+
|
|
|
+ self.fileSizeTipView.isHidden = true
|
|
|
+ NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideFileSizeTipView), object: nil)
|
|
|
+
|
|
|
+ }
|
|
|
+}
|