AIConfigWindowController.swift 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. //
  2. // AIConfigWindowController.swift
  3. // PDF Reader Pro Edition
  4. //
  5. // Created by Niehaoyu on 2024/1/17.
  6. //
  7. import Cocoa
  8. @objcMembers class AIConfigWindowController: NSWindowController, NSTextFieldDelegate, NSTextViewDelegate {
  9. @IBOutlet weak var contendBox: NSBox!
  10. @IBOutlet weak var titleLabel: NSTextField!
  11. @IBOutlet weak var titleTipBtn: KMButton!
  12. @IBOutlet weak var uploadBGView: NSView!
  13. @IBOutlet weak var uploadItemBox: KMBox!
  14. @IBOutlet weak var uploadDesLabel: NSTextField!
  15. @IBOutlet weak var uploadPDFBtn: KMButton!
  16. @IBOutlet weak var fileBGView: NSView!
  17. @IBOutlet weak var fileEmptyImage: NSImageView!
  18. @IBOutlet weak var fileActionView: NSView!
  19. @IBOutlet weak var fileActionLabel: NSTextField!
  20. @IBOutlet weak var fileActionBtn: KMButton!
  21. @IBOutlet weak var filePDFBGView: NSView!
  22. @IBOutlet weak var filePDFHoverBtn: KMButton!
  23. @IBOutlet weak var pdfCoverView: NSView!
  24. @IBOutlet weak var pdfCoverImageView: NSImageView!
  25. @IBOutlet weak var removePDFBtn: NSButton!
  26. @IBOutlet weak var inputTextCountLabel: NSTextField!
  27. @IBOutlet weak var fileSizeTipView: NSView!
  28. @IBOutlet weak var fileSizeTipLabel: NSTextField!
  29. @IBOutlet weak var pdfNameLbl: NSTextField!
  30. @IBOutlet var fileEmptyTextView: NSTextView!
  31. @IBOutlet weak var filePlaceholdLabel: NSTextField!
  32. @IBOutlet weak var fileContendViewTopConst: NSLayoutConstraint!
  33. @IBOutlet weak var translateSepLineView: NSView!
  34. @IBOutlet weak var translateConfigView: NSView!
  35. @IBOutlet weak var fromLanguageView: NSView!
  36. @IBOutlet weak var fromLanguageLabel: NSTextField!
  37. @IBOutlet weak var fromLanguageBtn: NSButton!
  38. @IBOutlet weak var toLanguageView: NSView!
  39. @IBOutlet weak var toLanguageLabel: NSTextField!
  40. @IBOutlet weak var toLanguageBtn: NSButton!
  41. @IBOutlet weak var translateResultView: NSView!
  42. @IBOutlet weak var translateResultLabel: NSTextField!
  43. @IBOutlet weak var translateResultSubLabel: NSTextField!
  44. @IBOutlet weak var resultBGView: NSView!
  45. @IBOutlet weak var resultEmptyImage: NSImageView!
  46. // @IBOutlet weak var resultLabel: NSTextField!
  47. @IBOutlet var aiResultTextView: NSTextView!
  48. @IBOutlet weak var cancelBtn: NSButton!
  49. @IBOutlet weak var saveAsTextBtn: NSButton!
  50. @IBOutlet weak var saveAsPDFBtn: NSButton!
  51. static var currentWindowController: AIConfigWindowController!
  52. var popOver: NSPopover!
  53. var configType: AIConfigType = .none
  54. var cancelHandle: ((_ windowVC: AIConfigWindowController) -> Void)?
  55. var fileActionenAble: Bool = false
  56. var aiConvertFinish: Bool = true
  57. var filePath: String = ""
  58. var fromLanguage: String = ""
  59. var toLanguage: String = ""
  60. var translateFinish: Bool = false
  61. var translateFile: Bool = false
  62. var translateResultPath: String = ""
  63. @objc static func currentWC() -> AIConfigWindowController {
  64. if currentWindowController != nil {
  65. return currentWindowController
  66. } else {
  67. let configWC: AIConfigWindowController = AIConfigWindowController.init(windowNibName: "AIConfigWindowController")
  68. currentWindowController = configWC;
  69. return currentWindowController
  70. }
  71. }
  72. override func windowDidLoad() {
  73. super.windowDidLoad()
  74. // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  75. self.titleLabel.font = NSFont.SFProTextSemiboldFont(13)
  76. self.titleTipBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  77. if mouseEntered {
  78. var tipString = NSLocalizedString("Recommended file size: 10M or less.", comment: "")
  79. if self.configType == .reWriting {
  80. tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
  81. } else if self.configType == .proofreading {
  82. tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
  83. } else if self.configType == .translate {
  84. tipString = NSLocalizedString("2 credit for every 10,000 characters", comment: "")
  85. }
  86. let popViewController = KMToolbarItemPopViewController.init()
  87. if self.popOver == nil {
  88. self.popOver = NSPopover.init()
  89. }
  90. self.popOver.contentViewController = popViewController
  91. self.popOver.animates = false
  92. self.popOver.behavior = .semitransient
  93. self.popOver.contentSize = (popViewController.view.frame.size)
  94. popViewController.updateWithHelpTip(helpTip: tipString)
  95. self.popOver.show(relativeTo: self.titleTipBtn.frame, of: (self.window?.contentView)!, preferredEdge: .maxY)
  96. } else {
  97. self.popOver.close()
  98. }
  99. }
  100. self.uploadItemBox.borderWidth = 0
  101. self.uploadItemBox.cornerRadius = 1
  102. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0()
  103. self.uploadDesLabel.font = NSFont.SFProTextRegularFont(13)
  104. self.uploadDesLabel.textColor = KMAppearance.KMColor_Layout_H0()
  105. self.uploadDesLabel.stringValue = NSLocalizedString("Choose", comment: "")
  106. self.uploadPDFBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  107. if mouseEntered {
  108. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S_1()
  109. } else {
  110. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0()
  111. }
  112. }
  113. self.fileBGView.wantsLayer = true
  114. self.fileBGView.layer?.cornerRadius = 6
  115. self.fileBGView.layer?.masksToBounds = true
  116. self.fileBGView.layer?.borderWidth = 1
  117. self.fileBGView.layer?.borderColor = KMAppearance.KMColor_Layout_L_2().cgColor
  118. self.fileSizeTipView.wantsLayer = true
  119. self.fileSizeTipLabel.font = NSFont.SFProTextRegularFont(12)
  120. self.fileSizeTipLabel.textColor = KMAppearance.KMColor_Layout_H0()
  121. self.fileSizeTipLabel.stringValue = NSLocalizedString("Please upload a file smaller than 10M.", comment: "")
  122. self.fileSizeTipView.isHidden = true
  123. self.pdfNameLbl.font = NSFont.SFProTextRegularFont(12)
  124. self.pdfNameLbl.textColor = KMAppearance.KMColor_Layout_H0()
  125. self.resultBGView.wantsLayer = true
  126. self.resultBGView.layer?.cornerRadius = 6
  127. self.resultBGView.layer?.masksToBounds = true
  128. self.fileActionView.wantsLayer = true
  129. self.fileActionView.layer?.cornerRadius = 1
  130. self.fileActionView.layer?.masksToBounds = true
  131. self.fileActionLabel.font = NSFont.SFProTextRegularFont(13)
  132. self.fileActionBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  133. if self.fileActionenAble == true {
  134. if mouseEntered {
  135. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M_1().cgColor
  136. } else {
  137. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor
  138. }
  139. }
  140. }
  141. self.translateSepLineView.wantsLayer = true
  142. self.pdfCoverView.wantsLayer = true
  143. self.pdfCoverView.layer?.cornerRadius = 2
  144. self.pdfCoverView.layer?.borderWidth = 2
  145. let countFormatter = TextFieldFormatter.init()
  146. countFormatter.setMaximumLength(2000)
  147. self.inputTextCountLabel.formatter = countFormatter
  148. self.inputTextCountLabel.delegate = self
  149. if KMAppearance.isDarkMode() {
  150. self.contendBox.fillColor = NSColor(red: 44/255, green: 44/255, blue: 44/255, alpha: 1)
  151. self.titleLabel.textColor = NSColor.white
  152. self.resultBGView.layer?.backgroundColor = NSColor(red: 49/255, green: 49/255, blue: 49/255, alpha: 1).cgColor
  153. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30()
  154. self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 251/255, green: 166/255, blue: 0, alpha: 1).cgColor
  155. self.translateSepLineView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
  156. self.translateResultView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
  157. self.pdfCoverView.layer?.borderColor = NSColor.white.withAlphaComponent(0.2).cgColor
  158. self.pdfCoverView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.1).cgColor
  159. } else {
  160. self.contendBox.fillColor = NSColor.white
  161. self.titleLabel.textColor = NSColor.black
  162. self.resultBGView.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
  163. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30()
  164. self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 253/255, green: 239/255, blue: 212/255, alpha: 1).cgColor
  165. self.translateSepLineView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor
  166. self.translateResultView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor
  167. self.pdfCoverView.layer?.borderColor = NSColor.black.withAlphaComponent(0.12).cgColor
  168. self.pdfCoverView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.04).cgColor
  169. }
  170. self.fileEmptyTextView.backgroundColor = NSColor.clear
  171. self.fileEmptyTextView.delegate = self
  172. self.fileEmptyTextView.isEditable = false
  173. self.fileEmptyTextView.frame = (self.fileEmptyTextView.enclosingScrollView?.contentView.bounds) ?? .zero
  174. self.fileEmptyTextView.autoresizingMask = [.width, .height]
  175. self.window?.makeFirstResponder(nil)
  176. 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"]
  177. let menu = NSMenu.init()
  178. for idx in 0...languages.count-1 {
  179. let string = languages[idx]
  180. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  181. menuItem.tag = 1000 + idx
  182. menu.addItem(menuItem)
  183. }
  184. self.fromLanguageView?.menu = menu
  185. self.fromLanguage = "Automatic"
  186. 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"]
  187. var toMenu = NSMenu.init()
  188. for idx in 0...languages.count-1 {
  189. let string = languages[idx]
  190. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  191. menuItem.tag = 3000 + idx
  192. toMenu.addItem(menuItem)
  193. }
  194. self.toLanguageView?.menu = toMenu
  195. self.toLanguage = "English"
  196. self.fromLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  197. self.toLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  198. self.fromLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  199. self.toLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  200. self.fromLanguageLabel.stringValue = self.fromLanguage
  201. self.toLanguageLabel.stringValue = self.toLanguage
  202. self.removePDFBtn.isHidden = true
  203. self.filePDFHoverBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  204. if mouseEntered {
  205. self.removePDFBtn.isHidden = false
  206. } else {
  207. self.removePDFBtn.isHidden = true
  208. }
  209. }
  210. self.cancelBtn.title = NSLocalizedString("Cancel", comment: "")
  211. self.saveAsTextBtn.title = NSLocalizedString("Save as TXT", comment: "")
  212. self.saveAsPDFBtn.title = NSLocalizedString("Save as PDF", comment: "")
  213. self.translateResultLabel.stringValue = NSLocalizedString("Translation Completion", comment: "")
  214. self.translateResultSubLabel.stringValue = NSLocalizedString("Click \"Save as PDF\" to save the result as a PDF file.", comment: "")
  215. self.translateResultView.isHidden = true
  216. self.translateResultView.wantsLayer = true
  217. if KMAppearance.isDarkMode() {
  218. self.translateResultLabel.textColor = NSColor.white
  219. self.translateResultSubLabel.textColor = NSColor.white
  220. } else {
  221. self.translateResultLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  222. self.translateResultSubLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  223. }
  224. self.translateResultLabel.font = NSFont.SFProTextSemiboldFont(13)
  225. self.translateResultSubLabel.font = NSFont.SFProTextRegularFont(13)
  226. self.aiResultTextView.font = NSFont.SFProTextRegularFont(14)
  227. self.aiResultTextView.textColor = KMAppearance.KMColor_Layout_H0()
  228. self.aiResultTextView.backgroundColor = NSColor.clear
  229. self.aiResultTextView.isEditable = false
  230. self.fileEmptyTextView.font = NSFont.SFProTextRegularFont(14)
  231. self.fileEmptyTextView.textColor = KMAppearance.KMColor_Layout_H0()
  232. }
  233. func getFileSize(atPath filePath : String) -> CGFloat {
  234. guard let dict = try? FileManager.default.attributesOfItem(atPath: filePath) as NSDictionary else {
  235. return 0
  236. }
  237. return CGFloat(dict.fileSize())
  238. }
  239. func refreshUI() -> Void {
  240. if self.configType == .summarize {
  241. self.titleLabel.stringValue = NSLocalizedString("AI Summarize", comment: "")
  242. self.fileEmptyImage.image = NSImage(named: "ai_summary_Empty")
  243. } else if self.configType == .reWriting {
  244. self.titleLabel.stringValue = NSLocalizedString("AI Rewrite", comment: "")
  245. self.fileEmptyImage.image = NSImage(named: "ai_rewriting_empty")
  246. } else if self.configType == .proofreading {
  247. self.titleLabel.stringValue = NSLocalizedString("AI Proofread", comment: "")
  248. self.fileEmptyImage.image = NSImage(named: "ai_proofreading_empty")
  249. } else if self.configType == .translate {
  250. self.titleLabel.stringValue = NSLocalizedString("AI Translate", comment: "")
  251. self.fileEmptyImage.image = NSImage(named: "ai_translate_empty")
  252. }
  253. self.translateResultView.isHidden = true
  254. self.translateConfigView.isHidden = true
  255. self.translateSepLineView.isHidden = true
  256. if self.configType == .reWriting ||
  257. self.configType == .proofreading {
  258. self.uploadBGView.isHidden = true
  259. self.fileContendViewTopConst.constant = 52
  260. self.filePDFBGView.isHidden = true
  261. } else {
  262. self.uploadBGView.isHidden = false
  263. self.fileContendViewTopConst.constant = 92
  264. self.filePDFBGView.isHidden = false
  265. if self.configType == .translate {
  266. self.fileContendViewTopConst.constant = 137
  267. self.translateConfigView.isHidden = false
  268. self.translateSepLineView.isHidden = false
  269. }
  270. }
  271. self.reloadData()
  272. }
  273. func clearData() -> Void {
  274. self.fileEmptyTextView.string = ""
  275. self.aiResultTextView.string = ""
  276. self.filePath = ""
  277. self.translateFinish = false
  278. self.translateFile = false
  279. self.cancelBtn.isEnabled = true
  280. }
  281. func reloadData() -> Void {
  282. self.clearData()
  283. self.fileEmptyTextView.isEditable = true
  284. self.inputTextCountLabel.isHidden = true
  285. if self.configType == .summarize {
  286. self.fileEmptyTextView.isEditable = false
  287. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by uploading a document (pdf).", comment: "")
  288. } else if self.configType == .reWriting {
  289. self.inputTextCountLabel.isHidden = false
  290. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  291. } else if self.configType == .proofreading {
  292. self.inputTextCountLabel.isHidden = false
  293. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  294. } else if self.configType == .translate {
  295. self.inputTextCountLabel.isHidden = false
  296. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text, or uploading a document (pdf).", comment: "")
  297. }
  298. self.updateActionViewUI()
  299. self.updateResultViewUI()
  300. }
  301. //MARK: 更新左侧UI
  302. func updateActionViewUI() -> Void{
  303. self.fileSizeTipView.isHidden = true
  304. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  305. self.enableActionView(true)
  306. self.filePlaceholdLabel.isHidden = true
  307. } else {
  308. self.enableActionView(false)
  309. self.filePlaceholdLabel.isHidden = false
  310. }
  311. if self.filePath.count > 0 {
  312. self.fileEmptyTextView.isHidden = true
  313. self.filePDFBGView.isHidden = false
  314. let document = CPDFDocument.init(url: URL(fileURLWithPath: self.filePath))
  315. if document?.isLocked == true || document == nil {
  316. self.filePath = ""
  317. self.updateActionViewUI()
  318. return
  319. }
  320. DispatchQueue.main.async {
  321. let page = document?.page(at: 0)
  322. let image = page?.thumbnail(of: page!.size)
  323. self.pdfCoverImageView.image = image
  324. }
  325. let filePathURL = URL(fileURLWithPath: self.filePath)
  326. let parentDirectory = filePathURL.lastPathComponent
  327. self.pdfNameLbl.stringValue = parentDirectory
  328. self.inputTextCountLabel.isHidden = true
  329. } else {
  330. self.fileEmptyTextView.isHidden = false
  331. self.filePDFBGView.isHidden = true
  332. self.pdfCoverImageView.image = nil
  333. self.inputTextCountLabel.isHidden = false
  334. if self.configType == .summarize {
  335. self.inputTextCountLabel.isHidden = true
  336. }
  337. }
  338. self.inputTextCountLabel.stringValue = String(format: "%ld", self.fileEmptyTextView.string.count) + "/2000"
  339. if self.fileEmptyTextView.string.count == 2000 {
  340. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Status_Err()
  341. } else {
  342. if KMAppearance.isDarkMode() {
  343. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30()
  344. } else {
  345. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30()
  346. }
  347. }
  348. if self.aiResultTextView.string.count > 0 && (self.fileEmptyTextView.string.count > 0 || self.filePath.count > 0){
  349. self.fileActionLabel.stringValue = NSLocalizedString("Redo (1 credit)", comment: "")
  350. if self.configType == .translate && self.filePath.isEmpty == false {
  351. self.fileActionLabel.stringValue = NSLocalizedString("Redo", comment: "")
  352. }
  353. } else {
  354. if self.configType == .summarize {
  355. self.fileActionLabel.stringValue = NSLocalizedString("Summarize", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  356. } else if self.configType == .reWriting {
  357. self.fileActionLabel.stringValue = NSLocalizedString("Rewrite", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  358. } else if self.configType == .proofreading {
  359. self.fileActionLabel.stringValue = NSLocalizedString("Proofread", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  360. } else if self.configType == .translate {
  361. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  362. if self.filePath.isEmpty == false {
  363. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "")
  364. }
  365. }
  366. }
  367. }
  368. func enableActionView(_ enable: Bool) -> Void {
  369. if enable {
  370. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor
  371. self.fileActionLabel.textColor = NSColor.white
  372. self.fileActionBtn.isEnabled = true
  373. } else {
  374. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().withAlphaComponent(0.4).cgColor
  375. self.fileActionLabel.textColor = NSColor.white.withAlphaComponent(0.4)
  376. self.fileActionBtn.isEnabled = false
  377. }
  378. self.fileActionenAble = enable
  379. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  380. self.filePlaceholdLabel.isHidden = true
  381. self.fileEmptyImage.isHidden = true
  382. } else {
  383. self.filePlaceholdLabel.isHidden = false
  384. self.fileEmptyImage.isHidden = false
  385. }
  386. }
  387. func updateResultViewUI() -> Void{
  388. if self.aiResultTextView.string.count > 0 {
  389. self.resultEmptyImage.isHidden = true
  390. self.saveAsTextBtn.isEnabled = true
  391. self.saveAsPDFBtn.isEnabled = true
  392. } else {
  393. self.resultEmptyImage.isHidden = false
  394. self.saveAsTextBtn.isEnabled = false
  395. self.saveAsPDFBtn.isEnabled = false
  396. }
  397. if self.configType == .translate && self.translateFinish == true {
  398. self.saveAsTextBtn.isEnabled = true
  399. self.saveAsPDFBtn.isEnabled = true
  400. if self.translateFile == true {
  401. self.translateResultView.isHidden = false
  402. self.saveAsTextBtn.isEnabled = false
  403. } else {
  404. self.translateResultView.isHidden = true
  405. }
  406. if translateResultView.isHidden == false {
  407. self.resultEmptyImage.isHidden = true
  408. }
  409. }
  410. }
  411. //MARK: WaitingView
  412. func addWaingView(_ view: NSView) -> Void {
  413. self.removeWaitingView(view)
  414. let wView = WaitingView.init(frame: view.bounds)
  415. wView.autoresizingMask = [.width, .height]
  416. view.addSubview(wView)
  417. wView.startAnimation()
  418. }
  419. func removeWaitingView(_ view: NSView) -> Void {
  420. for view in view.subviews {
  421. if view.className == WaitingView.className() {
  422. view.removeFromSuperview()
  423. break
  424. }
  425. }
  426. }
  427. func aiTranslateFinish() -> Void {
  428. self.aiConvertFinish = true
  429. self.removePDFBtn.isEnabled = true
  430. self.removeWaitingView(self.resultBGView)
  431. self.updateActionViewUI()
  432. }
  433. //MARK: AI-Action
  434. func aiSummarize() -> Void {
  435. if FileManager.default.fileExists(atPath: self.filePath) {
  436. self.aiResultTextView.string = ""
  437. self.addWaingView(self.resultBGView)
  438. self.enableActionView(false)
  439. self.removePDFBtn.isEnabled = false
  440. self.aiConvertFinish = false
  441. self.updateResultViewUI()
  442. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  443. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath, state: .extractSummaryFile) { wrapper in
  444. DispatchQueue.main.async {
  445. self.aiConvertFinish = true
  446. self.removePDFBtn.isEnabled = true
  447. self.removeWaitingView(self.resultBGView)
  448. self.updateActionViewUI()
  449. let success = wrapper.success
  450. if success {
  451. let resultStr = wrapper.content
  452. self.aiResultTextView.string = resultStr
  453. self.updateActionViewUI()
  454. self.updateResultViewUI()
  455. } else {
  456. let alert = NSAlert()
  457. alert.alertStyle = .critical
  458. alert.messageText = wrapper.content
  459. alert.runModal()
  460. return
  461. }
  462. }
  463. }
  464. }
  465. }
  466. func aiReWriting() -> Void {
  467. self.aiResultTextView.string = ""
  468. self.addWaingView(self.resultBGView)
  469. self.enableActionView(false)
  470. self.removePDFBtn.isEnabled = false
  471. self.aiConvertFinish = false
  472. self.updateResultViewUI()
  473. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  474. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  475. state: .rewrite) { wrapper in
  476. DispatchQueue.main.async {
  477. self.aiConvertFinish = true
  478. self.removePDFBtn.isEnabled = true
  479. self.removeWaitingView(self.resultBGView)
  480. self.updateActionViewUI()
  481. let success = wrapper.success
  482. if success {
  483. let resultStr = wrapper.content
  484. self.aiResultTextView.string = resultStr
  485. self.updateActionViewUI()
  486. self.updateResultViewUI()
  487. } else {
  488. let alert = NSAlert()
  489. alert.alertStyle = .critical
  490. alert.messageText = wrapper.content
  491. alert.runModal()
  492. return
  493. }
  494. }
  495. }
  496. }
  497. func aiProofreading() -> Void {
  498. self.aiResultTextView.string = ""
  499. self.addWaingView(self.resultBGView)
  500. self.enableActionView(false)
  501. self.removePDFBtn.isEnabled = false
  502. self.aiConvertFinish = false
  503. self.updateResultViewUI()
  504. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  505. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  506. state: .correctTypos) { wrapper in
  507. DispatchQueue.main.async {
  508. self.aiConvertFinish = true
  509. self.removePDFBtn.isEnabled = true
  510. self.removeWaitingView(self.resultBGView)
  511. self.updateActionViewUI()
  512. let success = wrapper.success
  513. if success {
  514. let resultStr = wrapper.content
  515. self.aiResultTextView.string = resultStr
  516. self.updateActionViewUI()
  517. self.updateResultViewUI()
  518. } else {
  519. let alert = NSAlert()
  520. alert.alertStyle = .critical
  521. alert.messageText = wrapper.content
  522. alert.runModal()
  523. return
  524. }
  525. }
  526. }
  527. }
  528. func aiTranslate() -> Void {
  529. }
  530. @objc func menuItemClick(_ item: NSMenuItem) {
  531. if item.tag < 2000 {
  532. self.fromLanguage = item.title
  533. } else {
  534. self.toLanguage = item.title
  535. }
  536. self.fromLanguageLabel.stringValue = self.fromLanguage
  537. self.toLanguageLabel.stringValue = self.toLanguage
  538. }
  539. //MARK: - IBAction
  540. @IBAction func chooseFileAction(_ sender: KMButton) {
  541. let openPanel = NSOpenPanel()
  542. openPanel.canChooseDirectories = true
  543. openPanel.canChooseFiles = true
  544. openPanel.allowsMultipleSelection = false;
  545. openPanel.allowedFileTypes = ["pdf"]
  546. openPanel.beginSheetModal(for: self.window!) { result in
  547. if result == .OK {
  548. let fileURL = openPanel.urls.first
  549. let fileSize = self.getFileSize(atPath: fileURL!.path)
  550. if fileSize/(1024*1024) > 10 {
  551. self.fileSizeTipView.isHidden = false
  552. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
  553. self.fileSizeTipView.isHidden = true
  554. }
  555. } else {
  556. self.filePath = fileURL!.path
  557. self.fileEmptyTextView.string = ""
  558. self.updateActionViewUI()
  559. }
  560. }
  561. }
  562. }
  563. @IBAction func chooseLanguageAction(_ sender: NSButton) {
  564. if sender == self.fromLanguageBtn {
  565. let menu = self.fromLanguageView?.menu
  566. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  567. } else if sender == self.toLanguageBtn {
  568. let menu = self.toLanguageView?.menu
  569. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  570. }
  571. }
  572. @IBAction func removePDFAction(_ sender: NSButton) {
  573. self.filePath = ""
  574. self.updateActionViewUI()
  575. self.updateResultViewUI()
  576. }
  577. @IBAction func startAIFunction(_ sender: KMButton) {
  578. let newStatus: Bool = KMCloudServer.isConnectionAvailable()
  579. if !newStatus {
  580. let alert = NSAlert()
  581. alert.alertStyle = .critical
  582. alert.messageText = NSLocalizedString("Please make sure your internet connection is available.", comment: "")
  583. alert.runModal()
  584. return
  585. }
  586. if self.configType == .summarize {
  587. self.aiSummarize()
  588. } else if self.configType == .reWriting {
  589. self.aiReWriting()
  590. } else if self.configType == .proofreading {
  591. self.aiProofreading()
  592. } else if self.configType == .translate {
  593. self.aiResultTextView.string = ""
  594. self.addWaingView(self.resultBGView)
  595. self.enableActionView(false)
  596. self.translateFinish = false
  597. self.translateResultView.isHidden = true
  598. self.updateResultViewUI()
  599. if self.filePath.count > 0 {
  600. self.translateFile = true
  601. self.removePDFBtn.isEnabled = false
  602. self.aiConvertFinish = false
  603. self.cancelBtn.isEnabled = false
  604. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath,
  605. state: .fileTranslate,
  606. from: self.fromLanguage,
  607. to: self.toLanguage) { wrapper in
  608. DispatchQueue.main.async {
  609. var success = wrapper.success
  610. let resultStr = wrapper.content
  611. if success == false && resultStr == "501" {
  612. success = true
  613. }
  614. if success {
  615. let translateTipWC = AITranslateTipWindowController.currentWC()
  616. let infoDict: NSDictionary = wrapper.result
  617. var credit: Int = 0
  618. if infoDict["credit"] != nil {
  619. credit = (infoDict["credit"] ?? "0") as! Int
  620. }
  621. var charCount: Int = 0
  622. if infoDict["charCount"] != nil {
  623. charCount = (infoDict["charCount"] ?? "0") as! Int
  624. if credit == 0 {
  625. if charCount < 10000 {
  626. credit = 2
  627. } else {
  628. credit = charCount/5000 + 1
  629. }
  630. }
  631. }
  632. translateTipWC.creditValid = true
  633. if resultStr == "501" {
  634. translateTipWC.creditValid = false
  635. }
  636. translateTipWC.spendCredits = credit
  637. translateTipWC.totalChart = charCount
  638. translateTipWC.actionHandle = {[weak self] wc, result in
  639. if result == true {
  640. self?.window?.endSheet(wc.window!)
  641. KMAIRequestServerManager.defaultManager.aiTranslationFileTranslateHandle(fileKey: resultStr,
  642. from: self!.fromLanguage,
  643. to: self!.toLanguage) { wrapper in
  644. let success = wrapper.success
  645. if success {
  646. DispatchQueue.main.async {
  647. self?.aiTranslateFinish()
  648. self?.cancelBtn.isEnabled = true
  649. self!.translateFinish = true
  650. self!.translateResultView.isHidden = false
  651. let resultStr = wrapper.content
  652. self!.translateResultPath = resultStr
  653. self!.updateActionViewUI()
  654. self!.updateResultViewUI()
  655. }
  656. } else {
  657. DispatchQueue.main.async {
  658. self?.aiTranslateFinish()
  659. self?.cancelBtn.isEnabled = true
  660. let alert = NSAlert()
  661. alert.alertStyle = .critical
  662. alert.messageText = wrapper.content
  663. alert.runModal()
  664. }
  665. return
  666. }
  667. }
  668. } else {
  669. self?.cancelBtn.isEnabled = true
  670. self?.aiTranslateFinish()
  671. self?.window?.endSheet(wc.window!)
  672. }
  673. }
  674. self.window?.beginSheet(translateTipWC.window!)
  675. translateTipWC.reloadData()
  676. } else {
  677. self.cancelBtn.isEnabled = true
  678. self.aiConvertFinish = true
  679. self.removePDFBtn.isEnabled = true
  680. self.removeWaitingView(self.resultBGView)
  681. self.updateActionViewUI()
  682. let alert = NSAlert()
  683. alert.alertStyle = .critical
  684. alert.messageText = wrapper.content
  685. alert.runModal()
  686. return
  687. }
  688. }
  689. };
  690. } else {
  691. self.translateFile = false
  692. self.removePDFBtn.isEnabled = false
  693. self.aiConvertFinish = false
  694. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  695. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  696. state: .textTranslate,
  697. from: self.fromLanguage,
  698. to: self.toLanguage) { wrapper in
  699. DispatchQueue.main.async {
  700. self.aiConvertFinish = true
  701. self.removePDFBtn.isEnabled = true
  702. self.removeWaitingView(self.resultBGView)
  703. self.updateActionViewUI()
  704. let success = wrapper.success
  705. if success {
  706. self.translateFinish = true
  707. self.translateResultView.isHidden = false
  708. let resultStr = wrapper.content
  709. self.aiResultTextView.string = resultStr
  710. self.updateActionViewUI()
  711. self.updateResultViewUI()
  712. } else {
  713. let alert = NSAlert()
  714. alert.alertStyle = .critical
  715. alert.messageText = wrapper.content
  716. alert.runModal()
  717. self.updateResultViewUI()
  718. return
  719. }
  720. }
  721. };
  722. }
  723. }
  724. }
  725. @IBAction func languageChangeAction(_ sender: Any) {
  726. let curLan = self.fromLanguage
  727. self.fromLanguage = self.toLanguage
  728. self.toLanguage = curLan
  729. self.fromLanguageLabel.stringValue = self.fromLanguage
  730. self.toLanguageLabel.stringValue = self.toLanguage
  731. }
  732. @IBAction func cancelAction(_ sender: NSButton) {
  733. DispatchQueue.main.async {
  734. self.removeWaitingView(self.resultBGView)
  735. self.updateActionViewUI()
  736. }
  737. if self.aiConvertFinish == false {
  738. let alert = NSAlert()
  739. alert.alertStyle = .critical
  740. 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: "")
  741. alert.addButton(withTitle: NSLocalizedString("Back ", comment: ""))
  742. alert.addButton(withTitle: NSLocalizedString("Confirm Cancel", comment: ""))
  743. let response = alert.runModal()
  744. if response.rawValue == 1001 {
  745. self.confirmCancelAction()
  746. }
  747. return
  748. }
  749. if self.aiResultTextView.string.count > 0 {
  750. if KMFunctionGuideWindowController.availableShow(.aiInfoResultSave) == true {
  751. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  752. let alert = NSAlert()
  753. alert.alertStyle = .critical
  754. alert.messageText = NSLocalizedString("The results are not saved. Do you want to save them in other formats?", comment: "")
  755. alert.addButton(withTitle: NSLocalizedString("Yes", comment: ""))
  756. alert.addButton(withTitle: NSLocalizedString("No", comment: ""))
  757. let response = alert.runModal()
  758. if response.rawValue == 1001 {
  759. self.cancelAction(self.cancelBtn)
  760. }
  761. return
  762. }
  763. }
  764. self.clearData()
  765. guard let callBack = self.cancelHandle else {
  766. return
  767. }
  768. callBack(self)
  769. }
  770. func confirmCancelAction() -> Void {
  771. self.clearData()
  772. guard let callBack = self.cancelHandle else {
  773. return
  774. }
  775. callBack(self)
  776. }
  777. @IBAction func saveAsTextAction(_ sender: Any) {
  778. let fileName = "Untitled"
  779. let outputSavePanel = NSSavePanel()
  780. outputSavePanel.allowedFileTypes = ["txt"]
  781. outputSavePanel.nameFieldStringValue = fileName
  782. let result = outputSavePanel.runModal()
  783. if result == .OK {
  784. let filePath = outputSavePanel.url?.path
  785. do {
  786. try self.aiResultTextView.string.write(to: outputSavePanel.url!, atomically: true, encoding: .utf8)
  787. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  788. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  789. } catch {
  790. }
  791. }
  792. }
  793. @IBAction func saveAsPDFAction(_ sender: Any) {
  794. let fileName = "Untitled"
  795. let outputSavePanel = NSSavePanel()
  796. outputSavePanel.allowedFileTypes = ["pdf"]
  797. outputSavePanel.nameFieldStringValue = fileName
  798. let result = outputSavePanel.runModal()
  799. if result == .OK {
  800. let filePath = outputSavePanel.url?.path
  801. do {
  802. if self.translateFile == true {
  803. do {
  804. if self.translateResultPath.count > 0 {
  805. if FileManager.default.fileExists(atPath: filePath!) {
  806. do {
  807. try FileManager.default.removeItem(atPath: filePath!)
  808. // print("删除旧文件成功")
  809. } catch {
  810. // print("删除旧文件失败:\(error)")
  811. }
  812. }
  813. try FileManager.default.copyItem(atPath: self.translateResultPath, toPath: filePath!)
  814. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  815. }
  816. } catch {
  817. }
  818. } else {
  819. let text = self.aiResultTextView.string
  820. self.createPDF(from: text, saveTo: filePath!)
  821. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  822. }
  823. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  824. } catch {
  825. }
  826. }
  827. }
  828. func textDidChange(_ notification: Notification) {
  829. if let textView = notification.object as? NSTextView, textView == self.fileEmptyTextView {
  830. // 获取文本字段的当前字符数
  831. let currentText = textView.string
  832. let currentCount = currentText.count
  833. // 如果超过最大字符数,将文本截断为最大字符数,并设置回文本字段
  834. if currentCount > 2000 {
  835. let endIndex = currentText.index(currentText.startIndex, offsetBy: 2000)
  836. let truncatedText = String(currentText[..<endIndex])
  837. textView.string = truncatedText
  838. }
  839. }
  840. self.updateActionViewUI()
  841. }
  842. override func mouseDown(with event: NSEvent) {
  843. super.mouseDown(with: event)
  844. self.window?.makeFirstResponder(nil)
  845. }
  846. func createPDF(from text: String, saveTo path: String) {
  847. let attributedText = NSAttributedString(string: text)
  848. let pdfData = NSMutableData()
  849. let pdfConsumer = CGDataConsumer(data: pdfData)!
  850. var mediaBox = CGRect(x: 0, y: 0, width: 612, height: 792) // 设置页面尺寸,这里使用默认的美国信纸尺寸
  851. let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
  852. pdfContext.beginPage(mediaBox: &mediaBox)
  853. pdfContext.textMatrix = .identity
  854. pdfContext.setShouldAntialias(true)
  855. let frame = mediaBox.insetBy(dx: 20, dy: 20) // 设置文字边距
  856. let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
  857. let framePath = CGPath(rect: frame, transform: nil)
  858. let frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), framePath, nil)
  859. CTFrameDraw(frameRef, pdfContext)
  860. pdfContext.endPage()
  861. pdfContext.closePDF()
  862. pdfData.write(toFile: path, atomically: true)
  863. print("PDF 文件已保存至:\(path)")
  864. }
  865. }